暂无描述
您最多选择25个主题 主题必须以字母或数字开头,可以包含连字符 (-),并且长度不得超过35个字符

TrackActions.cs 18KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527
  1. using System.Collections.Generic;
  2. using System.Linq;
  3. using JetBrains.Annotations;
  4. using UnityEditor.Timeline.Actions;
  5. using UnityEngine;
  6. using UnityEngine.Playables;
  7. using UnityEngine.Timeline;
  8. namespace UnityEditor.Timeline
  9. {
  10. [MenuEntry("Edit in Animation Window", MenuPriority.TrackActionSection.editInAnimationWindow)]
  11. class EditTrackInAnimationWindow : TrackAction
  12. {
  13. public static bool Do(TrackAsset track)
  14. {
  15. AnimationClip clipToEdit = null;
  16. AnimationTrack animationTrack = track as AnimationTrack;
  17. if (animationTrack != null)
  18. {
  19. if (!animationTrack.CanConvertToClipMode())
  20. return false;
  21. clipToEdit = animationTrack.infiniteClip;
  22. }
  23. else if (track.hasCurves)
  24. {
  25. clipToEdit = track.curves;
  26. }
  27. if (clipToEdit == null)
  28. return false;
  29. GameObject gameObject = null;
  30. if (TimelineEditor.inspectedDirector != null)
  31. gameObject = TimelineUtility.GetSceneGameObject(TimelineEditor.inspectedDirector, track);
  32. var timeController = TimelineAnimationUtilities.CreateTimeController(CreateTimeControlClipData(track));
  33. TimelineAnimationUtilities.EditAnimationClipWithTimeController(clipToEdit, timeController, gameObject);
  34. return true;
  35. }
  36. public override ActionValidity Validate(IEnumerable<TrackAsset> tracks)
  37. {
  38. if (!tracks.Any())
  39. return ActionValidity.Invalid;
  40. var firstTrack = tracks.First();
  41. if (firstTrack is AnimationTrack)
  42. {
  43. var animTrack = firstTrack as AnimationTrack;
  44. if (animTrack.CanConvertToClipMode())
  45. return ActionValidity.Valid;
  46. }
  47. else if (firstTrack.hasCurves)
  48. {
  49. return ActionValidity.Valid;
  50. }
  51. return ActionValidity.NotApplicable;
  52. }
  53. public override bool Execute(IEnumerable<TrackAsset> tracks)
  54. {
  55. return Do(tracks.First());
  56. }
  57. static TimelineWindowTimeControl.ClipData CreateTimeControlClipData(TrackAsset track)
  58. {
  59. var data = new TimelineWindowTimeControl.ClipData();
  60. data.track = track;
  61. data.start = track.start;
  62. data.duration = track.duration;
  63. return data;
  64. }
  65. }
  66. [MenuEntry("Lock selected track only", MenuPriority.TrackActionSection.lockSelected)]
  67. class LockSelectedTrack : TrackAction, IMenuName
  68. {
  69. public static readonly string LockSelectedTrackOnlyText = L10n.Tr("Lock selected track only");
  70. public static readonly string UnlockSelectedTrackOnlyText = L10n.Tr("Unlock selected track only");
  71. public string menuName { get; private set; }
  72. public override ActionValidity Validate(IEnumerable<TrackAsset> tracks)
  73. {
  74. UpdateMenuName(tracks);
  75. if (tracks.Any(track => TimelineUtility.IsLockedFromGroup(track) || track is GroupTrack || !track.subTracksObjects.Any()))
  76. return ActionValidity.NotApplicable;
  77. return ActionValidity.Valid;
  78. }
  79. public override bool Execute(IEnumerable<TrackAsset> tracks)
  80. {
  81. if (!tracks.Any()) return false;
  82. var hasUnlockedTracks = tracks.Any(x => !x.locked);
  83. Lock(tracks.Where(p => !(p is GroupTrack)).ToArray(), hasUnlockedTracks);
  84. return true;
  85. }
  86. void UpdateMenuName(IEnumerable<TrackAsset> tracks)
  87. {
  88. menuName = tracks.All(t => t.locked) ? UnlockSelectedTrackOnlyText : LockSelectedTrackOnlyText;
  89. }
  90. public static void Lock(TrackAsset[] tracks, bool shouldlock)
  91. {
  92. if (tracks.Length == 0)
  93. return;
  94. foreach (var track in tracks.Where(t => !TimelineUtility.IsLockedFromGroup(t)))
  95. {
  96. TimelineUndo.PushUndo(track, L10n.Tr("Lock Tracks"));
  97. track.locked = shouldlock;
  98. }
  99. TimelineEditor.Refresh(RefreshReason.WindowNeedsRedraw);
  100. }
  101. }
  102. [MenuEntry("Lock", MenuPriority.TrackActionSection.lockTrack)]
  103. [Shortcut(Shortcuts.Timeline.toggleLock)]
  104. class LockTrack : TrackAction, IMenuName
  105. {
  106. static readonly string k_LockText = L10n.Tr("Lock");
  107. static readonly string k_UnlockText = L10n.Tr("Unlock");
  108. public string menuName { get; private set; }
  109. void UpdateMenuName(IEnumerable<TrackAsset> tracks)
  110. {
  111. menuName = tracks.Any(x => !x.locked) ? k_LockText : k_UnlockText;
  112. }
  113. public override bool Execute(IEnumerable<TrackAsset> tracks)
  114. {
  115. if (!tracks.Any()) return false;
  116. var hasUnlockedTracks = tracks.Any(x => !x.locked);
  117. SetLockState(tracks, hasUnlockedTracks);
  118. return true;
  119. }
  120. public override ActionValidity Validate(IEnumerable<TrackAsset> tracks)
  121. {
  122. UpdateMenuName(tracks);
  123. tracks = tracks.RemoveTimelineMarkerTrackFromList(TimelineEditor.inspectedAsset);
  124. if (!tracks.Any())
  125. return ActionValidity.NotApplicable;
  126. if (tracks.Any(TimelineUtility.IsLockedFromGroup))
  127. return ActionValidity.Invalid;
  128. return ActionValidity.Valid;
  129. }
  130. public static void SetLockState(IEnumerable<TrackAsset> tracks, bool shouldLock)
  131. {
  132. if (!tracks.Any())
  133. return;
  134. foreach (var track in tracks)
  135. {
  136. if (TimelineUtility.IsLockedFromGroup(track))
  137. continue;
  138. if (track as GroupTrack == null)
  139. SetLockState(track.GetChildTracks().ToArray(), shouldLock);
  140. TimelineUndo.PushUndo(track, L10n.Tr("Lock Tracks"));
  141. track.locked = shouldLock;
  142. }
  143. // find the tracks we've locked. unselect anything locked and remove recording.
  144. foreach (var track in tracks)
  145. {
  146. if (TimelineUtility.IsLockedFromGroup(track) || !track.locked)
  147. continue;
  148. var flattenedChildTracks = track.GetFlattenedChildTracks();
  149. foreach (var i in track.clips)
  150. SelectionManager.Remove(i);
  151. track.UnarmForRecord();
  152. foreach (var child in flattenedChildTracks)
  153. {
  154. SelectionManager.Remove(child);
  155. child.UnarmForRecord();
  156. foreach (var clip in child.GetClips())
  157. SelectionManager.Remove(clip);
  158. }
  159. }
  160. // no need to rebuild, just repaint (including inspectors)
  161. InspectorWindow.RepaintAllInspectors();
  162. TimelineEditor.Refresh(RefreshReason.WindowNeedsRedraw);
  163. }
  164. }
  165. [UsedImplicitly]
  166. [MenuEntry("Show Markers", MenuPriority.TrackActionSection.showHideMarkers)]
  167. [ActiveInMode(TimelineModes.Default | TimelineModes.ReadOnly)]
  168. class ShowHideMarkers : TrackAction, IMenuChecked
  169. {
  170. public bool isChecked { get; private set; }
  171. void UpdateCheckedStatus(IEnumerable<TrackAsset> tracks)
  172. {
  173. isChecked = tracks.All(x => x.GetShowMarkers());
  174. }
  175. public override ActionValidity Validate(IEnumerable<TrackAsset> tracks)
  176. {
  177. UpdateCheckedStatus(tracks);
  178. if (tracks.Any(x => x is GroupTrack) || tracks.Any(t => t.GetMarkerCount() == 0))
  179. return ActionValidity.NotApplicable;
  180. if (tracks.Any(t => t.lockedInHierarchy))
  181. {
  182. return ActionValidity.Invalid;
  183. }
  184. return ActionValidity.Valid;
  185. }
  186. public override bool Execute(IEnumerable<TrackAsset> tracks)
  187. {
  188. if (!tracks.Any()) return false;
  189. var hasUnlockedTracks = tracks.Any(x => !x.GetShowMarkers());
  190. ShowHide(tracks, hasUnlockedTracks);
  191. return true;
  192. }
  193. static void ShowHide(IEnumerable<TrackAsset> tracks, bool shouldLock)
  194. {
  195. if (!tracks.Any())
  196. return;
  197. foreach (var track in tracks)
  198. track.SetShowTrackMarkers(shouldLock);
  199. TimelineEditor.Refresh(RefreshReason.WindowNeedsRedraw);
  200. }
  201. }
  202. [MenuEntry("Mute selected track only", MenuPriority.TrackActionSection.muteSelected), UsedImplicitly]
  203. class MuteSelectedTrack : TrackAction, IMenuName
  204. {
  205. public static readonly string MuteSelectedText = L10n.Tr("Mute selected track only");
  206. public static readonly string UnmuteSelectedText = L10n.Tr("Unmute selected track only");
  207. public string menuName { get; private set; }
  208. public override ActionValidity Validate(IEnumerable<TrackAsset> tracks)
  209. {
  210. UpdateMenuName(tracks);
  211. if (tracks.Any(track => TimelineUtility.IsParentMuted(track) || track is GroupTrack || !track.subTracksObjects.Any()))
  212. return ActionValidity.NotApplicable;
  213. return ActionValidity.Valid;
  214. }
  215. public override bool Execute(IEnumerable<TrackAsset> tracks)
  216. {
  217. if (!tracks.Any())
  218. return false;
  219. var hasUnmutedTracks = tracks.Any(x => !x.muted);
  220. Mute(tracks.Where(p => !(p is GroupTrack)).ToArray(), hasUnmutedTracks);
  221. return true;
  222. }
  223. void UpdateMenuName(IEnumerable<TrackAsset> tracks)
  224. {
  225. menuName = tracks.All(t => t.muted) ? UnmuteSelectedText : MuteSelectedText;
  226. }
  227. public static void Mute(TrackAsset[] tracks, bool shouldMute)
  228. {
  229. if (tracks.Length == 0)
  230. return;
  231. foreach (var track in tracks.Where(t => !TimelineUtility.IsParentMuted(t)))
  232. {
  233. TimelineUndo.PushUndo(track, L10n.Tr("Mute Tracks"));
  234. track.muted = shouldMute;
  235. }
  236. TimelineEditor.Refresh(RefreshReason.ContentsModified);
  237. }
  238. }
  239. [MenuEntry("Mute", MenuPriority.TrackActionSection.mute)]
  240. [Shortcut(Shortcuts.Timeline.toggleMute)]
  241. class MuteTrack : TrackAction, IMenuName
  242. {
  243. static readonly string k_MuteText = L10n.Tr("Mute");
  244. static readonly string k_UnMuteText = L10n.Tr("Unmute");
  245. public string menuName { get; private set; }
  246. void UpdateMenuName(IEnumerable<TrackAsset> tracks)
  247. {
  248. menuName = tracks.Any(x => !x.muted) ? k_MuteText : k_UnMuteText;
  249. }
  250. public override bool Execute(IEnumerable<TrackAsset> tracks)
  251. {
  252. if (!tracks.Any() || tracks.Any(TimelineUtility.IsParentMuted))
  253. return false;
  254. var hasUnmutedTracks = tracks.Any(x => !x.muted);
  255. Mute(tracks, hasUnmutedTracks);
  256. return true;
  257. }
  258. public override ActionValidity Validate(IEnumerable<TrackAsset> tracks)
  259. {
  260. UpdateMenuName(tracks);
  261. if (tracks.Any(TimelineUtility.IsLockedFromGroup))
  262. return ActionValidity.Invalid;
  263. return ActionValidity.Valid;
  264. }
  265. public static void Mute(IEnumerable<TrackAsset> tracks, bool shouldMute)
  266. {
  267. if (!tracks.Any())
  268. return;
  269. foreach (var track in tracks)
  270. {
  271. if (track as GroupTrack == null)
  272. Mute(track.GetChildTracks().ToArray(), shouldMute);
  273. TimelineUndo.PushUndo(track, L10n.Tr("Mute Tracks"));
  274. track.muted = shouldMute;
  275. }
  276. TimelineEditor.Refresh(RefreshReason.ContentsModified);
  277. }
  278. }
  279. class DeleteTracks : TrackAction
  280. {
  281. public static void Do(TimelineAsset timeline, TrackAsset track)
  282. {
  283. SelectionManager.Remove(track);
  284. TrackModifier.DeleteTrack(timeline, track);
  285. }
  286. public override ActionValidity Validate(IEnumerable<TrackAsset> tracks) => ActionValidity.Valid;
  287. public override bool Execute(IEnumerable<TrackAsset> tracks)
  288. {
  289. tracks = tracks.RemoveTimelineMarkerTrackFromList(TimelineEditor.inspectedAsset);
  290. // disable preview mode so deleted tracks revert to default state
  291. // Case 956129: Disable preview mode _before_ deleting the tracks, since clip data is still needed
  292. TimelineEditor.state.previewMode = false;
  293. TimelineAnimationUtilities.UnlinkAnimationWindowFromTracks(tracks);
  294. foreach (var track in tracks)
  295. Do(TimelineEditor.inspectedAsset, track);
  296. TimelineEditor.Refresh(RefreshReason.ContentsAddedOrRemoved);
  297. return true;
  298. }
  299. }
  300. class CopyTracksToClipboard : TrackAction
  301. {
  302. public static bool Do(TrackAsset[] tracks)
  303. {
  304. var action = new CopyTracksToClipboard();
  305. return action.Execute(tracks);
  306. }
  307. public override ActionValidity Validate(IEnumerable<TrackAsset> tracks) => ActionValidity.Valid;
  308. public override bool Execute(IEnumerable<TrackAsset> tracks)
  309. {
  310. tracks = tracks.RemoveTimelineMarkerTrackFromList(TimelineEditor.inspectedAsset);
  311. TimelineEditor.clipboard.CopyTracks(tracks);
  312. return true;
  313. }
  314. }
  315. class DuplicateTracks : TrackAction
  316. {
  317. public override ActionValidity Validate(IEnumerable<TrackAsset> tracks) => ActionValidity.Valid;
  318. public override bool Execute(IEnumerable<TrackAsset> tracks)
  319. {
  320. tracks = tracks.RemoveTimelineMarkerTrackFromList(TimelineEditor.inspectedAsset);
  321. if (tracks.Any())
  322. {
  323. SelectionManager.RemoveTimelineSelection();
  324. }
  325. foreach (var track in TrackExtensions.FilterTracks(tracks))
  326. {
  327. var newTrack = track.Duplicate(TimelineEditor.inspectedDirector, TimelineEditor.inspectedDirector);
  328. //Add all duplicated tracks to selection
  329. SelectionManager.Add(newTrack);
  330. foreach (var childTrack in newTrack.GetFlattenedChildTracks())
  331. {
  332. SelectionManager.Add(childTrack);
  333. }
  334. //Duplicate bindings for tracks and subtracks
  335. if (TimelineEditor.inspectedDirector != null)
  336. {
  337. DuplicateBindings(track, newTrack, TimelineEditor.inspectedDirector);
  338. }
  339. }
  340. TimelineEditor.Refresh(RefreshReason.ContentsAddedOrRemoved);
  341. return true;
  342. }
  343. internal static void DuplicateBindings(TrackAsset track, TrackAsset newTrack, PlayableDirector director)
  344. {
  345. var originalTracks = track.GetFlattenedChildTracks().Append(track);
  346. var newTracks = newTrack.GetFlattenedChildTracks().Append(newTrack);
  347. var toBind = new List<Tuple<TrackAsset, Object>>();
  348. // Collect all track bindings to duplicate
  349. var originalIt = originalTracks.GetEnumerator();
  350. var newIt = newTracks.GetEnumerator();
  351. while (originalIt.MoveNext() && newIt.MoveNext())
  352. {
  353. var binding = director.GetGenericBinding(originalIt.Current);
  354. if (binding != null)
  355. toBind.Add(new Tuple<TrackAsset, Object>(newIt.Current, binding));
  356. }
  357. //Only create Director undo if there are bindings to duplicate
  358. if (toBind.Count > 0)
  359. TimelineUndo.PushUndo(TimelineEditor.inspectedDirector, L10n.Tr("Duplicate"));
  360. //Assign bindings for all tracks after undo.
  361. foreach (var binding in toBind)
  362. {
  363. TimelineEditor.inspectedDirector.SetGenericBinding(binding.Item1, binding.Item2);
  364. }
  365. }
  366. }
  367. [MenuEntry("Remove Invalid Markers", MenuPriority.TrackActionSection.removeInvalidMarkers), UsedImplicitly]
  368. class RemoveInvalidMarkersAction : TrackAction
  369. {
  370. public override ActionValidity Validate(IEnumerable<TrackAsset> tracks)
  371. {
  372. if (tracks.Any(target => target != null && target.GetMarkerCount() != target.GetMarkersRaw().Count()))
  373. return ActionValidity.Valid;
  374. return ActionValidity.NotApplicable;
  375. }
  376. public override bool Execute(IEnumerable<TrackAsset> tracks)
  377. {
  378. bool anyRemoved = false;
  379. foreach (var target in tracks)
  380. {
  381. var invalids = target.GetMarkersRaw().Where(x => !(x is IMarker)).ToList();
  382. foreach (var m in invalids)
  383. {
  384. anyRemoved = true;
  385. target.DeleteMarkerRaw(m);
  386. }
  387. }
  388. if (anyRemoved)
  389. TimelineEditor.Refresh(RefreshReason.ContentsAddedOrRemoved);
  390. return anyRemoved;
  391. }
  392. }
  393. [Shortcut(Shortcuts.Timeline.collapseTrack)]
  394. [UsedImplicitly]
  395. class CollapseTrackAction : TrackAction
  396. {
  397. public override ActionValidity Validate(IEnumerable<TrackAsset> tracks)
  398. {
  399. var collapsibleTracks = tracks.Where(track => track.subTracksObjects.Any());
  400. if (!collapsibleTracks.Any())
  401. return ActionValidity.NotApplicable;
  402. if (collapsibleTracks.All(track => track.IsCollapsed()))
  403. return ActionValidity.NotApplicable;
  404. return ActionValidity.Valid;
  405. }
  406. public override bool Execute(IEnumerable<TrackAsset> tracks)
  407. {
  408. return KeyboardNavigation.TryCollapse(tracks.Where(track => track.subTracksObjects.Any() && !track.IsCollapsed()));
  409. }
  410. }
  411. [Shortcut(Shortcuts.Timeline.expandTrack)]
  412. [UsedImplicitly]
  413. class ExpandTrackAction : TrackAction
  414. {
  415. public override ActionValidity Validate(IEnumerable<TrackAsset> tracks)
  416. {
  417. var collapsibleTracks = tracks.Where(track => track.subTracksObjects.Any());
  418. if (!collapsibleTracks.Any())
  419. return ActionValidity.NotApplicable;
  420. if (collapsibleTracks.All(track => !track.IsCollapsed()))
  421. return ActionValidity.NotApplicable;
  422. return ActionValidity.Valid;
  423. }
  424. public override bool Execute(IEnumerable<TrackAsset> tracks)
  425. {
  426. return KeyboardNavigation.TryExpand(tracks.Where(track => track.subTracksObjects.Any() && track.IsCollapsed()));
  427. }
  428. }
  429. }