TimelineKeyboardNavigation.cs 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387
  1. using System;
  2. using System.Collections.Generic;
  3. using System.Linq;
  4. using UnityEditor.IMGUI.Controls;
  5. using UnityEngine.Timeline;
  6. namespace UnityEditor.Timeline
  7. {
  8. static class KeyboardNavigation
  9. {
  10. public static void FrameTrackHeader(TreeViewItem treeItem = null)
  11. {
  12. if (TrackHeadActive())
  13. treeItem = treeItem ?? SelectionManager.SelectedTrackGUI().Last();
  14. else
  15. {
  16. var item = GetVisibleSelectedItems().LastOrDefault();
  17. treeItem = TimelineWindow.instance.allTracks.FirstOrDefault(
  18. x => item != null && x.track == item.parentTrack);
  19. }
  20. if (treeItem != null)
  21. TimelineWindow.instance.treeView.FrameItem(treeItem);
  22. }
  23. public static bool TrackHeadActive()
  24. {
  25. return SelectionManager.SelectedTracks().Any(x => x.IsVisibleRecursive()) && !ClipAreaActive();
  26. }
  27. public static bool ClipAreaActive()
  28. {
  29. return GetVisibleSelectedItems().Any();
  30. }
  31. public static IEnumerable<ITimelineItem> GetVisibleSelectedItems()
  32. {
  33. return SelectionManager.SelectedItems().Where(x => x.parentTrack.IsVisibleRecursive());
  34. }
  35. public static IEnumerable<TimelineTrackBaseGUI> GetVisibleTracks()
  36. {
  37. return TimelineWindow.instance.allTracks.Where(x => x.track.IsVisibleRecursive());
  38. }
  39. static TrackAsset PreviousTrack(this TrackAsset track)
  40. {
  41. var uiOrderTracks = GetVisibleTracks().Select(t => t.track).ToList();
  42. var selIdx = uiOrderTracks.IndexOf(track);
  43. return selIdx > 0 ? uiOrderTracks[selIdx - 1] : null;
  44. }
  45. static TrackAsset NextTrack(this TrackAsset track)
  46. {
  47. var uiOrderTracks = GetVisibleTracks().Select(t => t.track).ToList();
  48. var selIdx = uiOrderTracks.IndexOf(track);
  49. return selIdx < uiOrderTracks.Count - 1 && selIdx != -1 ? uiOrderTracks[selIdx + 1] : null;
  50. }
  51. static ITimelineItem PreviousItem(this ITimelineItem item, bool clipOnly)
  52. {
  53. var items = item.parentTrack.GetItems().ToArray();
  54. if (clipOnly)
  55. {
  56. items = items.Where(x => x is ClipItem).ToArray();
  57. }
  58. else
  59. {
  60. items = items.Where(x => x is MarkerItem).ToArray();
  61. }
  62. var idx = Array.IndexOf(items, item);
  63. return idx > 0 ? items[idx - 1] : null;
  64. }
  65. static ITimelineItem NextItem(this ITimelineItem item, bool clipOnly)
  66. {
  67. var items = item.parentTrack.GetItems().ToArray();
  68. if (clipOnly)
  69. {
  70. items = items.Where(x => x is ClipItem).ToArray();
  71. }
  72. else
  73. {
  74. items = items.Where(x => x is MarkerItem).ToArray();
  75. }
  76. var idx = Array.IndexOf(items, item);
  77. return idx < items.Length - 1 ? items[idx + 1] : null;
  78. }
  79. static bool FilterItems(ref List<ITimelineItem> items)
  80. {
  81. var clipOnly = false;
  82. if (items.Any(x => x is ClipItem))
  83. {
  84. items = items.Where(x => x is ClipItem).ToList();
  85. clipOnly = true;
  86. }
  87. return clipOnly;
  88. }
  89. static ITimelineItem GetClosestItem(TrackAsset track, ITimelineItem refItem)
  90. {
  91. var start = refItem.start;
  92. var end = refItem.end;
  93. var items = track.GetItems().ToList();
  94. if (refItem is ClipItem)
  95. {
  96. items = items.Where(x => x is ClipItem).ToList();
  97. }
  98. else
  99. {
  100. items = items.Where(x => x is MarkerItem).ToList();
  101. }
  102. if (!items.Any())
  103. return null;
  104. ITimelineItem ret = null;
  105. var scoreToBeat = double.NegativeInfinity;
  106. foreach (var item in items)
  107. {
  108. // test for overlap
  109. var low = Math.Max(item.start, start);
  110. var high = Math.Min(item.end, end);
  111. if (low <= high)
  112. {
  113. var score = high - low;
  114. if (score >= scoreToBeat)
  115. {
  116. scoreToBeat = score;
  117. ret = item;
  118. }
  119. }
  120. }
  121. return ret;
  122. }
  123. public static bool FocusFirstVisibleItem(WindowState state,
  124. IEnumerable<TrackAsset> focusTracks = null)
  125. {
  126. var timeRange = state.timeAreaShownRange;
  127. var tracks = focusTracks ?? TimelineWindow.instance.treeView.visibleTracks.Where(x => x.IsVisibleRecursive() && x.GetItems().Any());
  128. var items = tracks.SelectMany(t => t.GetItems().OfType<ClipItem>().Where(x => x.end >= timeRange.x && x.end <= timeRange.y ||
  129. x.start >= timeRange.x && x.start <= timeRange.y)).ToList();
  130. var itemFullyInView = items.Where(x => x.end >= timeRange.x && x.end <= timeRange.y &&
  131. x.start >= timeRange.x && x.start <= timeRange.y);
  132. var itemToSelect = itemFullyInView.FirstOrDefault() ?? items.FirstOrDefault();
  133. if (itemToSelect != null)
  134. {
  135. SelectionManager.SelectOnly(itemToSelect);
  136. return true;
  137. }
  138. return false;
  139. }
  140. public static bool CollapseGroup(WindowState state)
  141. {
  142. if (TrackHeadActive())
  143. {
  144. var quit = false;
  145. foreach (var track in SelectionManager.SelectedTracks())
  146. {
  147. if (!track.GetChildTracks().Any())
  148. continue;
  149. if (!quit && !track.GetCollapsed())
  150. quit = true;
  151. track.SetCollapsed(true);
  152. }
  153. if (quit)
  154. {
  155. state.Refresh();
  156. return true;
  157. }
  158. var selectedTrack = SelectionManager.SelectedTracks().LastOrDefault();
  159. var parent = selectedTrack != null ? selectedTrack.parent as TrackAsset : null;
  160. if (parent)
  161. {
  162. SelectionManager.SelectOnly(parent);
  163. FrameTrackHeader(GetVisibleTracks().First(x => x.track == parent));
  164. return true;
  165. }
  166. }
  167. return false;
  168. }
  169. public static bool SelectLeftItem(WindowState state, bool shift = false)
  170. {
  171. if (ClipAreaActive())
  172. {
  173. var items = SelectionManager.SelectedItems().ToList();
  174. var clipOnly = FilterItems(ref items);
  175. var item = items.Last();
  176. var prev = item.PreviousItem(clipOnly);
  177. if (prev != null)
  178. {
  179. if (shift)
  180. {
  181. if (SelectionManager.Contains(prev))
  182. SelectionManager.Remove(item);
  183. SelectionManager.Add(prev);
  184. }
  185. else
  186. SelectionManager.SelectOnly(prev);
  187. TimelineHelpers.FrameItems(state, new[] {prev});
  188. }
  189. else if (item != null && !shift && item.parentTrack != state.editSequence.asset.markerTrack)
  190. SelectionManager.SelectOnly(item.parentTrack);
  191. return true;
  192. }
  193. return false;
  194. }
  195. public static bool SelectRightItem(WindowState state, bool shift = false)
  196. {
  197. if (ClipAreaActive())
  198. {
  199. var items = SelectionManager.SelectedItems().ToList();
  200. var clipOnly = FilterItems(ref items);
  201. var item = items.Last();
  202. var next = item.NextItem(clipOnly);
  203. if (next != null)
  204. {
  205. if (shift)
  206. {
  207. if (SelectionManager.Contains(next))
  208. SelectionManager.Remove(item);
  209. SelectionManager.Add(next);
  210. }
  211. else
  212. SelectionManager.SelectOnly(next);
  213. TimelineHelpers.FrameItems(state, new[] {next});
  214. return true;
  215. }
  216. }
  217. return false;
  218. }
  219. public static bool UnCollapseGroup(WindowState state)
  220. {
  221. if (TrackHeadActive())
  222. {
  223. var quit = false;
  224. foreach (var track in SelectionManager.SelectedTracks())
  225. {
  226. if (!track.GetChildTracks().Any()) continue;
  227. if (!quit && track.GetCollapsed())
  228. quit = true;
  229. track.SetCollapsed(false);
  230. }
  231. if (quit)
  232. {
  233. state.Refresh();
  234. return true;
  235. }
  236. // Transition to Clip area
  237. var visibleTracks = GetVisibleTracks().Select(x => x.track).ToList();
  238. var idx = visibleTracks.IndexOf(SelectionManager.SelectedTracks().Last());
  239. ITimelineItem item = null;
  240. for (var i = idx; i < visibleTracks.Count; ++i)
  241. {
  242. var items = visibleTracks[i].GetItems().OfType<ClipItem>();
  243. if (!items.Any())
  244. continue;
  245. item = items.First();
  246. break;
  247. }
  248. if (item != null)
  249. {
  250. SelectionManager.SelectOnly(item);
  251. TimelineHelpers.FrameItems(state, new[] {item});
  252. return true;
  253. }
  254. }
  255. return false;
  256. }
  257. public static bool SelectUpTrack(bool shift = false)
  258. {
  259. if (TrackHeadActive())
  260. {
  261. var prevTrack = PreviousTrack(SelectionManager.SelectedTracks().Last());
  262. if (prevTrack != null)
  263. {
  264. if (shift)
  265. {
  266. if (SelectionManager.Contains(prevTrack))
  267. SelectionManager.Remove(SelectionManager.SelectedTracks().Last());
  268. SelectionManager.Add(prevTrack);
  269. }
  270. else
  271. SelectionManager.SelectOnly(prevTrack);
  272. FrameTrackHeader(GetVisibleTracks().First(x => x.track == prevTrack));
  273. }
  274. return true;
  275. }
  276. return false;
  277. }
  278. public static bool SelectUpItem(WindowState state)
  279. {
  280. if (ClipAreaActive())
  281. {
  282. var refItem = SelectionManager.SelectedItems().Last();
  283. var prevTrack = refItem.parentTrack.PreviousTrack();
  284. while (prevTrack != null)
  285. {
  286. var selectionItem = GetClosestItem(prevTrack, refItem);
  287. if (selectionItem == null)
  288. {
  289. prevTrack = prevTrack.PreviousTrack();
  290. continue;
  291. }
  292. SelectionManager.SelectOnly(selectionItem);
  293. TimelineHelpers.FrameItems(state, new[] {selectionItem});
  294. FrameTrackHeader(GetVisibleTracks().First(x => x.track == selectionItem.parentTrack));
  295. break;
  296. }
  297. return true;
  298. }
  299. return false;
  300. }
  301. public static bool SelectDownTrack(bool shift = false)
  302. {
  303. if (TrackHeadActive())
  304. {
  305. var nextTrack = SelectionManager.SelectedTracks().Last().NextTrack();
  306. if (nextTrack != null)
  307. {
  308. if (shift)
  309. {
  310. if (SelectionManager.Contains(nextTrack))
  311. SelectionManager.Remove(SelectionManager.SelectedTracks().Last());
  312. SelectionManager.Add(nextTrack);
  313. }
  314. else
  315. SelectionManager.SelectOnly(nextTrack);
  316. FrameTrackHeader(GetVisibleTracks().First(x => x.track == nextTrack));
  317. }
  318. return true;
  319. }
  320. return false;
  321. }
  322. public static bool SelectDownItem(WindowState state)
  323. {
  324. if (ClipAreaActive())
  325. {
  326. var refItem = SelectionManager.SelectedItems().Last();
  327. var nextTrack = refItem.parentTrack.NextTrack();
  328. while (nextTrack != null)
  329. {
  330. var selectionItem = GetClosestItem(nextTrack, refItem);
  331. if (selectionItem == null)
  332. {
  333. nextTrack = nextTrack.NextTrack();
  334. continue;
  335. }
  336. SelectionManager.SelectOnly(selectionItem);
  337. TimelineHelpers.FrameItems(state, new[] {selectionItem});
  338. FrameTrackHeader(GetVisibleTracks().First(x => x.track == selectionItem.parentTrack));
  339. break;
  340. }
  341. return true;
  342. }
  343. return false;
  344. }
  345. }
  346. }