暫無描述
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

TimelineContextMenu.cs 17KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381
  1. using System;
  2. using System.Collections.Generic;
  3. using System.Linq;
  4. using UnityEditor.Timeline.Actions;
  5. using UnityEngine;
  6. using UnityEngine.Timeline;
  7. using Object = UnityEngine.Object;
  8. namespace UnityEditor.Timeline
  9. {
  10. static class SequencerContextMenu
  11. {
  12. static class Styles
  13. {
  14. public static readonly string addItemFromAssetTemplate = L10n.Tr("Add {0} From {1}");
  15. public static readonly string addSingleItemFromAssetTemplate = L10n.Tr("Add From {1}");
  16. public static readonly string addItemTemplate = L10n.Tr("Add {0}");
  17. public static readonly string typeSelectorTemplate = L10n.Tr("Select {0}");
  18. public static readonly string trackGroup = L10n.Tr("Track Group");
  19. public static readonly string trackSubGroup = L10n.Tr("Track Sub-Group");
  20. public static readonly string addTrackLayer = L10n.Tr("Add Layer");
  21. public static readonly string layerName = L10n.Tr("Layer {0}");
  22. }
  23. public static void ShowNewTracksContextMenu(ICollection<TrackAsset> tracks, WindowState state)
  24. {
  25. var menu = new GenericMenu();
  26. List<MenuActionItem> items = new List<MenuActionItem>(100);
  27. BuildNewTracksContextMenu(items, tracks, state);
  28. ActionManager.BuildMenu(menu, items);
  29. menu.ShowAsContext();
  30. }
  31. public static void ShowNewTracksContextMenu(ICollection<TrackAsset> tracks, WindowState state, Rect rect)
  32. {
  33. var menu = new GenericMenu();
  34. List<MenuActionItem> items = new List<MenuActionItem>(100);
  35. BuildNewTracksContextMenu(items, tracks, state);
  36. ActionManager.BuildMenu(menu, items);
  37. menu.DropDown(rect);
  38. }
  39. public static void ShowTrackContextMenu(Vector2? mousePosition)
  40. {
  41. var items = new List<MenuActionItem>();
  42. var menu = new GenericMenu();
  43. BuildTrackContextMenu(items, mousePosition);
  44. ActionManager.BuildMenu(menu, items);
  45. menu.ShowAsContext();
  46. }
  47. public static void ShowItemContextMenu(Vector2 mousePosition)
  48. {
  49. var menu = new GenericMenu();
  50. var items = new List<MenuActionItem>();
  51. BuildItemContextMenu(items, mousePosition);
  52. ActionManager.BuildMenu(menu, items);
  53. menu.ShowAsContext();
  54. }
  55. public static void BuildItemContextMenu(List<MenuActionItem> items, Vector2 mousePosition)
  56. {
  57. ActionManager.GetMenuEntries(ActionManager.TimelineActions, mousePosition, items);
  58. ActionManager.GetMenuEntries(ActionManager.ClipActions, items);
  59. ActionManager.GetMenuEntries(ActionManager.MarkerActions, items);
  60. var clips = TimelineEditor.selectedClips;
  61. if (clips.Length > 0)
  62. AddMarkerMenuCommands(items, clips.Select(c => c.GetParentTrack()).Distinct().ToList(), TimelineHelpers.GetCandidateTime(mousePosition));
  63. }
  64. public static void BuildNewTracksContextMenu(List<MenuActionItem> menuItems, ICollection<TrackAsset> parentTracks, WindowState state, string format = null)
  65. {
  66. if (parentTracks == null)
  67. parentTracks = new TrackAsset[0];
  68. if (string.IsNullOrEmpty(format))
  69. format = "{0}";
  70. // Add Group or SubGroup
  71. var title = string.Format(format, parentTracks.Any(t => t != null) ? Styles.trackSubGroup : Styles.trackGroup);
  72. var menuState = ActionValidity.Valid;
  73. if (state.editSequence.isReadOnly)
  74. menuState = ActionValidity.Invalid;
  75. if (parentTracks.Any() && parentTracks.Any(t => t != null && t.lockedInHierarchy))
  76. menuState = ActionValidity.Invalid;
  77. GenericMenu.MenuFunction command = () =>
  78. {
  79. SelectionManager.Clear();
  80. if (parentTracks.Count == 0)
  81. Selection.Add(TimelineHelpers.CreateTrack<GroupTrack>(null, title));
  82. foreach (var parentTrack in parentTracks)
  83. Selection.Add(TimelineHelpers.CreateTrack<GroupTrack>(parentTrack, title));
  84. TimelineEditor.Refresh(RefreshReason.ContentsAddedOrRemoved);
  85. };
  86. menuItems.Add(
  87. new MenuActionItem()
  88. {
  89. category = string.Empty,
  90. entryName = title,
  91. isActiveInMode = true,
  92. priority = MenuPriority.AddItem.addGroup,
  93. state = menuState,
  94. isChecked = false,
  95. callback = command
  96. }
  97. );
  98. var allTypes = TypeUtility.AllTrackTypes().Where(x => x != typeof(GroupTrack) && !TypeUtility.IsHiddenInMenu(x)).ToList();
  99. int builtInPriority = MenuPriority.AddItem.addTrack;
  100. int customPriority = MenuPriority.AddItem.addCustomTrack;
  101. foreach (var trackType in allTypes)
  102. {
  103. var trackItemType = trackType;
  104. command = () =>
  105. {
  106. SelectionManager.Clear();
  107. if (parentTracks.Count == 0)
  108. SelectionManager.Add(TimelineHelpers.CreateTrack((Type)trackItemType, null));
  109. foreach (var parentTrack in parentTracks)
  110. SelectionManager.Add(TimelineHelpers.CreateTrack((Type)trackItemType, parentTrack));
  111. };
  112. menuItems.Add(
  113. new MenuActionItem()
  114. {
  115. category = TimelineHelpers.GetTrackCategoryName(trackType),
  116. entryName = string.Format(format, TimelineHelpers.GetTrackMenuName(trackItemType)),
  117. isActiveInMode = true,
  118. priority = TypeUtility.IsBuiltIn(trackType) ? builtInPriority++ : customPriority++,
  119. state = menuState,
  120. callback = command
  121. }
  122. );
  123. }
  124. }
  125. public static void BuildTrackContextMenu(List<MenuActionItem> items, Vector2? mousePosition)
  126. {
  127. var tracks = SelectionManager.SelectedTracks().ToArray();
  128. if (tracks.Length == 0)
  129. return;
  130. ActionManager.GetMenuEntries(ActionManager.TimelineActions, mousePosition, items);
  131. ActionManager.GetMenuEntries(ActionManager.TrackActions, items);
  132. AddLayeredTrackCommands(items, tracks);
  133. var first = tracks.First().GetType();
  134. var allTheSame = tracks.All(t => t.GetType() == first);
  135. if (allTheSame)
  136. {
  137. if (first != typeof(GroupTrack))
  138. {
  139. var candidateTime = TimelineHelpers.GetCandidateTime(mousePosition, tracks);
  140. AddClipMenuCommands(items, tracks, candidateTime);
  141. AddMarkerMenuCommands(items, tracks, candidateTime);
  142. }
  143. else
  144. {
  145. BuildNewTracksContextMenu(items, tracks, TimelineWindow.instance.state, Styles.addItemTemplate);
  146. }
  147. }
  148. }
  149. static void AddLayeredTrackCommands(List<MenuActionItem> menuItems, ICollection<TrackAsset> tracks)
  150. {
  151. if (tracks.Count == 0)
  152. return;
  153. var layeredType = tracks.First().GetType();
  154. // animation tracks have a special menu.
  155. if (layeredType == typeof(AnimationTrack))
  156. return;
  157. // must implement ILayerable
  158. if (!typeof(UnityEngine.Timeline.ILayerable).IsAssignableFrom(layeredType))
  159. return;
  160. if (tracks.Any(t => t.GetType() != layeredType))
  161. return;
  162. // only supported on the master track no nesting.
  163. if (tracks.Any(t => t.isSubTrack))
  164. return;
  165. var enabled = tracks.All(t => t != null && !t.lockedInHierarchy) && !TimelineWindow.instance.state.editSequence.isReadOnly;
  166. int priority = MenuPriority.AddTrackMenu.addLayerTrack;
  167. GenericMenu.MenuFunction menuCallback = () =>
  168. {
  169. foreach (var track in tracks)
  170. TimelineHelpers.CreateTrack(layeredType, track, string.Format(Styles.layerName, track.GetChildTracks().Count() + 1));
  171. };
  172. var entryName = Styles.addTrackLayer;
  173. menuItems.Add(
  174. new MenuActionItem()
  175. {
  176. category = string.Empty,
  177. entryName = entryName,
  178. isActiveInMode = true,
  179. priority = priority++,
  180. state = enabled ? ActionValidity.Valid : ActionValidity.Invalid,
  181. callback = menuCallback
  182. }
  183. );
  184. }
  185. static void AddClipMenuCommands(List<MenuActionItem> menuItems, ICollection<TrackAsset> tracks, double candidateTime)
  186. {
  187. if (!tracks.Any())
  188. return;
  189. var trackAsset = tracks.First();
  190. var trackType = trackAsset.GetType();
  191. if (tracks.Any(t => t.GetType() != trackType))
  192. return;
  193. var enabled = tracks.All(t => t != null && !t.lockedInHierarchy) && !TimelineWindow.instance.state.editSequence.isReadOnly;
  194. var assetTypes = TypeUtility.GetPlayableAssetsHandledByTrack(trackType);
  195. var visibleAssetTypes = TypeUtility.GetVisiblePlayableAssetsHandledByTrack(trackType);
  196. // skips the name if there is only a single type
  197. var commandNameTemplate = assetTypes.Count() == 1 ? Styles.addSingleItemFromAssetTemplate : Styles.addItemFromAssetTemplate;
  198. int builtInPriority = MenuPriority.AddItem.addClip;
  199. int customPriority = MenuPriority.AddItem.addCustomClip;
  200. foreach (var assetType in assetTypes)
  201. {
  202. var assetItemType = assetType;
  203. var category = TimelineHelpers.GetItemCategoryName(assetType);
  204. Action<Object> onObjectChanged = obj =>
  205. {
  206. if (obj != null)
  207. {
  208. foreach (var t in tracks)
  209. {
  210. TimelineHelpers.CreateClipOnTrack(assetItemType, obj, t, candidateTime);
  211. }
  212. }
  213. };
  214. foreach (var objectReference in TypeUtility.ObjectReferencesForType(assetType))
  215. {
  216. var isSceneReference = objectReference.isSceneReference;
  217. var dataType = objectReference.type;
  218. GenericMenu.MenuFunction menuCallback = () =>
  219. {
  220. ObjectSelector.get.Show(null, dataType, null, isSceneReference, null, (obj) => onObjectChanged(obj), null);
  221. ObjectSelector.get.titleContent = EditorGUIUtility.TrTextContent(string.Format(Styles.typeSelectorTemplate, TypeUtility.GetDisplayName(dataType)));
  222. };
  223. menuItems.Add(
  224. new MenuActionItem()
  225. {
  226. category = category,
  227. entryName = string.Format(commandNameTemplate, TypeUtility.GetDisplayName(assetType), TypeUtility.GetDisplayName(objectReference.type)),
  228. isActiveInMode = true,
  229. priority = TypeUtility.IsBuiltIn(assetType) ? builtInPriority++ : customPriority++,
  230. state = enabled ? ActionValidity.Valid : ActionValidity.Invalid,
  231. callback = menuCallback
  232. }
  233. );
  234. }
  235. }
  236. foreach (var assetType in visibleAssetTypes)
  237. {
  238. var assetItemType = assetType;
  239. var category = TimelineHelpers.GetItemCategoryName(assetType);
  240. var commandName = string.Format(Styles.addItemTemplate, TypeUtility.GetDisplayName(assetType));
  241. GenericMenu.MenuFunction command = () =>
  242. {
  243. foreach (var t in tracks)
  244. {
  245. TimelineHelpers.CreateClipOnTrack(assetItemType, t, candidateTime);
  246. }
  247. };
  248. menuItems.Add(
  249. new MenuActionItem()
  250. {
  251. category = category,
  252. entryName = commandName,
  253. isActiveInMode = true,
  254. priority = TypeUtility.IsBuiltIn(assetItemType) ? builtInPriority++ : customPriority++,
  255. state = enabled ? ActionValidity.Valid : ActionValidity.Invalid,
  256. callback = command
  257. }
  258. );
  259. }
  260. }
  261. static void AddMarkerMenuCommands(List<MenuActionItem> menu, IEnumerable<Type> markerTypes, Action<Type, Object> addMarkerCommand, bool enabled)
  262. {
  263. int builtInPriority = MenuPriority.AddItem.addMarker;
  264. int customPriority = MenuPriority.AddItem.addCustomMarker;
  265. foreach (var markerType in markerTypes)
  266. {
  267. var markerItemType = markerType;
  268. string category = TimelineHelpers.GetItemCategoryName(markerItemType);
  269. menu.Add(
  270. new MenuActionItem()
  271. {
  272. category = category,
  273. entryName = string.Format(Styles.addItemTemplate, TypeUtility.GetDisplayName(markerType)),
  274. isActiveInMode = true,
  275. priority = TypeUtility.IsBuiltIn(markerType) ? builtInPriority++ : customPriority++,
  276. state = enabled ? ActionValidity.Valid : ActionValidity.Invalid,
  277. callback = () => addMarkerCommand(markerItemType, null)
  278. }
  279. );
  280. foreach (var objectReference in TypeUtility.ObjectReferencesForType(markerType))
  281. {
  282. var isSceneReference = objectReference.isSceneReference;
  283. GenericMenu.MenuFunction menuCallback = () =>
  284. {
  285. Type assetDataType = objectReference.type;
  286. ObjectSelector.get.titleContent = EditorGUIUtility.TrTextContent(string.Format(Styles.typeSelectorTemplate, TypeUtility.GetDisplayName(assetDataType)));
  287. ObjectSelector.get.Show(null, assetDataType, null, isSceneReference, null, obj =>
  288. {
  289. if (obj != null)
  290. addMarkerCommand(markerItemType, obj);
  291. }, null);
  292. };
  293. menu.Add(
  294. new MenuActionItem
  295. {
  296. category = TimelineHelpers.GetItemCategoryName(markerItemType),
  297. entryName = string.Format(Styles.addItemFromAssetTemplate, TypeUtility.GetDisplayName(markerType), TypeUtility.GetDisplayName(objectReference.type)),
  298. isActiveInMode = true,
  299. priority = TypeUtility.IsBuiltIn(markerType) ? builtInPriority++ : customPriority++,
  300. state = enabled ? ActionValidity.Valid : ActionValidity.Invalid,
  301. callback = menuCallback
  302. }
  303. );
  304. }
  305. }
  306. }
  307. static void AddMarkerMenuCommands(List<MenuActionItem> menuItems, ICollection<TrackAsset> tracks, double candidateTime)
  308. {
  309. if (tracks.Count == 0)
  310. return;
  311. var enabled = tracks.All(t => !t.lockedInHierarchy) && !TimelineWindow.instance.state.editSequence.isReadOnly;
  312. var addMarkerCommand = new Action<Type, Object>((type, obj) => AddMarkersCallback(tracks, type, candidateTime, obj));
  313. AddMarkerMenuCommands(menuItems, tracks, addMarkerCommand, enabled);
  314. }
  315. static void AddMarkerMenuCommands(List<MenuActionItem> menuItems, ICollection<TrackAsset> tracks, Action<Type, Object> command, bool enabled)
  316. {
  317. var markerTypes = TypeUtility.GetBuiltInMarkerTypes().Union(TypeUtility.GetUserMarkerTypes());
  318. if (tracks != null)
  319. markerTypes = markerTypes.Where(x => tracks.All(track => (track == null) || TypeUtility.DoesTrackSupportMarkerType(track, x))); // null track indicates marker track to be created
  320. AddMarkerMenuCommands(menuItems, markerTypes, command, enabled);
  321. }
  322. static void AddMarkersCallback(ICollection<TrackAsset> targets, Type markerType, double time, Object obj)
  323. {
  324. SelectionManager.Clear();
  325. foreach (var target in targets)
  326. {
  327. var marker = TimelineHelpers.CreateMarkerOnTrack(markerType, obj, target, time);
  328. SelectionManager.Add(marker);
  329. }
  330. TimelineEditor.Refresh(RefreshReason.ContentsAddedOrRemoved);
  331. }
  332. }
  333. }