A game about forced loneliness, made by TACStudios
at master 461 lines 16 kB view raw
1using System; 2using System.Collections.Generic; 3using System.Linq; 4using UnityEditor.IMGUI.Controls; 5using UnityEngine.Timeline; 6 7namespace UnityEditor.Timeline 8{ 9 static class KeyboardNavigation 10 { 11 public static void FrameTrackHeader(TreeViewItem treeItem = null) 12 { 13 if (TrackHeadActive()) 14 treeItem = treeItem ?? SelectionManager.SelectedTrackGUI().Last(); 15 else 16 { 17 var item = GetVisibleSelectedItems().LastOrDefault(); 18 treeItem = TimelineWindow.instance.allTracks.FirstOrDefault( 19 x => item != null && x.track == item.parentTrack); 20 } 21 22 if (treeItem != null) 23 TimelineWindow.instance.treeView.FrameItem(treeItem); 24 } 25 26 public static bool TrackHeadActive() 27 { 28 return SelectionManager.SelectedTracks().Any(x => x.IsVisibleInHierarchy()) && !ClipAreaActive(); 29 } 30 31 public static bool ClipAreaActive() 32 { 33 return GetVisibleSelectedItems().Any(); 34 } 35 36 public static IEnumerable<ITimelineItem> GetVisibleSelectedItems() 37 { 38 return SelectionManager.SelectedItems().Where(x => x.parentTrack.IsVisibleInHierarchy()); 39 } 40 41 public static IEnumerable<TimelineTrackBaseGUI> GetVisibleTracks() 42 { 43 return TimelineWindow.instance.allTracks.Where(x => x.track.IsVisibleInHierarchy()); 44 } 45 46 static TrackAsset PreviousTrack(this TrackAsset track) 47 { 48 var uiOrderTracks = GetVisibleTracks().Select(t => t.track).ToList(); 49 var selIdx = uiOrderTracks.IndexOf(track); 50 return selIdx > 0 ? uiOrderTracks[selIdx - 1] : null; 51 } 52 53 static TrackAsset NextTrack(this TrackAsset track) 54 { 55 var uiOrderTracks = GetVisibleTracks().Select(t => t.track).ToList(); 56 var selIdx = uiOrderTracks.IndexOf(track); 57 return selIdx < uiOrderTracks.Count - 1 && selIdx != -1 ? uiOrderTracks[selIdx + 1] : null; 58 } 59 60 static ITimelineItem PreviousItem(this ITimelineItem item, bool clipOnly) 61 { 62 var items = item.parentTrack.GetItems().ToArray(); 63 if (clipOnly) 64 { 65 items = items.Where(x => x is ClipItem).ToArray(); 66 } 67 else 68 { 69 items = items.Where(x => x is MarkerItem).ToArray(); 70 } 71 72 var idx = Array.IndexOf(items, item); 73 return idx > 0 ? items[idx - 1] : null; 74 } 75 76 static ITimelineItem NextItem(this ITimelineItem item, bool clipOnly) 77 { 78 var items = item.parentTrack.GetItems().ToArray(); 79 if (clipOnly) 80 { 81 items = items.Where(x => x is ClipItem).ToArray(); 82 } 83 else 84 { 85 items = items.Where(x => x is MarkerItem).ToArray(); 86 } 87 88 var idx = Array.IndexOf(items, item); 89 return idx < items.Length - 1 ? items[idx + 1] : null; 90 } 91 92 static bool FilterItems(ref List<ITimelineItem> items) 93 { 94 var clipOnly = false; 95 if (items.Any(x => x is ClipItem)) 96 { 97 items = items.Where(x => x is ClipItem).ToList(); 98 clipOnly = true; 99 } 100 101 return clipOnly; 102 } 103 104 static ITimelineItem GetClosestItem(TrackAsset track, ITimelineItem refItem) 105 { 106 var start = refItem.start; 107 var end = refItem.end; 108 var items = track.GetItems().ToList(); 109 110 if (refItem is ClipItem) 111 { 112 items = items.Where(x => x is ClipItem).ToList(); 113 } 114 else 115 { 116 items = items.Where(x => x is MarkerItem).ToList(); 117 } 118 119 if (!items.Any()) 120 return null; 121 ITimelineItem ret = null; 122 var scoreToBeat = double.NegativeInfinity; 123 124 foreach (var item in items) 125 { 126 // test for overlap 127 var low = Math.Max(item.start, start); 128 var high = Math.Min(item.end, end); 129 if (low <= high) 130 { 131 var score = high - low; 132 if (score >= scoreToBeat) 133 { 134 scoreToBeat = score; 135 ret = item; 136 } 137 } 138 } 139 140 return ret; 141 } 142 143 public static bool FocusFirstVisibleItem(IEnumerable<TrackAsset> focusTracks = null) 144 { 145 var timeRange = TimelineEditor.visibleTimeRange; 146 147 var tracks = focusTracks ?? TimelineWindow.instance.treeView.visibleTracks.Where(x => x.IsVisibleInHierarchy() && x.GetItems().Any()); 148 var items = tracks.SelectMany(t => t.GetItems().OfType<ClipItem>().Where(x => x.end >= timeRange.x && x.end <= timeRange.y || 149 x.start >= timeRange.x && x.start <= timeRange.y)).ToList(); 150 var itemFullyInView = items.Where(x => x.end >= timeRange.x && x.end <= timeRange.y && 151 x.start >= timeRange.x && x.start <= timeRange.y); 152 var itemToSelect = itemFullyInView.FirstOrDefault() ?? items.FirstOrDefault(); 153 if (itemToSelect != null && !itemToSelect.parentTrack.lockedInHierarchy) 154 { 155 SelectionManager.SelectOnly(itemToSelect); 156 return true; 157 } 158 return false; 159 } 160 161 public static bool NavigateLeft(IEnumerable<TrackAsset> tracks) 162 { 163 if (!TrackHeadActive()) 164 return false; 165 166 if (TryCollapse(tracks)) 167 return true; 168 169 return SelectAndShowParentTrack(tracks.LastOrDefault()); 170 } 171 172 /// <summary> 173 /// Tries to collapse any track from a list of tracks 174 /// </summary> 175 /// <param name="tracks"></param> 176 /// <returns>true if any were collapsed, false otherwise</returns> 177 public static bool TryCollapse(IEnumerable<TrackAsset> tracks) 178 { 179 var didCollapse = false; 180 181 foreach (TrackAsset track in tracks) 182 { 183 if (!track.GetChildTracks().Any()) 184 continue; 185 186 if (!track.IsCollapsed()) 187 { 188 didCollapse = true; 189 track.SetCollapsed(true); 190 } 191 } 192 193 if (didCollapse) 194 { 195 TimelineEditor.window.treeView.Reload(); 196 return true; 197 } 198 199 return false; 200 } 201 202 public static bool ToggleCollapseGroup(IEnumerable<TrackAsset> tracks) 203 { 204 if (!TrackHeadActive()) 205 return false; 206 207 var didChange = false; 208 209 foreach (TrackAsset track in tracks) 210 { 211 if (!track.GetChildTracks().Any()) 212 continue; 213 214 track.SetCollapsed(!track.IsCollapsed()); 215 didChange = true; 216 } 217 218 if (didChange) 219 TimelineEditor.window.treeView.Reload(); 220 221 return didChange; 222 } 223 224 static bool SelectAndShowParentTrack(TrackAsset track) 225 { 226 TrackAsset parent = track != null ? track.parent as TrackAsset : null; 227 if (parent) 228 { 229 SelectionManager.SelectOnly(parent); 230 FrameTrackHeader(GetVisibleTracks().FirstOrDefault(x => x.track == parent)); 231 return true; 232 } 233 234 return false; 235 } 236 237 public static bool SelectLeftItem(bool shift = false) 238 { 239 if (ClipAreaActive()) 240 { 241 var items = SelectionManager.SelectedItems().ToList(); 242 var clipOnly = FilterItems(ref items); 243 244 var item = items.Last(); 245 var prev = item.PreviousItem(clipOnly); 246 if (prev != null && !prev.parentTrack.lockedInHierarchy) 247 { 248 if (shift) 249 { 250 if (SelectionManager.Contains(prev)) 251 SelectionManager.Remove(item); 252 SelectionManager.Add(prev); 253 } 254 else 255 SelectionManager.SelectOnly(prev); 256 TimelineHelpers.FrameItems(new[] { prev }); 257 } 258 else if (item != null && !shift && item.parentTrack != TimelineEditor.inspectedAsset.markerTrack) 259 SelectionManager.SelectOnly(item.parentTrack); 260 return true; 261 } 262 return false; 263 } 264 265 public static bool SelectRightItem(bool shift = false) 266 { 267 if (ClipAreaActive()) 268 { 269 var items = SelectionManager.SelectedItems().ToList(); 270 var clipOnly = FilterItems(ref items); 271 272 var item = items.Last(); 273 var next = item.NextItem(clipOnly); 274 if (next != null && !next.parentTrack.lockedInHierarchy) 275 { 276 if (shift) 277 { 278 if (SelectionManager.Contains(next)) 279 SelectionManager.Remove(item); 280 SelectionManager.Add(next); 281 } 282 else 283 SelectionManager.SelectOnly(next); 284 TimelineHelpers.FrameItems(new[] { next }); 285 return true; 286 } 287 } 288 return false; 289 } 290 291 public static bool NavigateRight(IEnumerable<TrackAsset> tracks) 292 { 293 if (!TrackHeadActive()) 294 return false; 295 296 if (TryExpand(tracks)) 297 return true; 298 299 return TrySelectFirstChild(tracks); 300 } 301 302 /// <summary> 303 /// Tries to expand from a list of tracks 304 /// </summary> 305 /// <param name="tracks"></param> 306 /// <returns>true if any expanded, false otherwise</returns> 307 public static bool TryExpand(IEnumerable<TrackAsset> tracks) 308 { 309 var didExpand = false; 310 foreach (TrackAsset track in tracks) 311 { 312 if (!track.GetChildTracks().Any()) 313 continue; 314 315 if (track.IsCollapsed()) 316 { 317 didExpand = true; 318 track.SetCollapsed(false); 319 } 320 } 321 322 if (didExpand) 323 { 324 TimelineEditor.window.treeView.Reload(); 325 } 326 327 return didExpand; 328 } 329 330 /// <summary> 331 /// Tries to select the first clip from a list of tracks 332 /// </summary> 333 /// <param name="tracks"></param> 334 /// <returns>true if any expanded, false otherwise</returns> 335 public static bool TrySelectFirstChild(IEnumerable<TrackAsset> tracks) 336 { 337 foreach (var track in tracks) 338 { 339 //Try to navigate in group tracks first 340 if (track is GroupTrack) 341 { 342 if (track.GetChildTracks().Any()) 343 { 344 SelectionManager.SelectOnly(track.GetChildTracks().First()); 345 return true; 346 } 347 //Group tracks should not halt navigation 348 continue; 349 } 350 //if track is locked or has no clips, do nothing 351 if (track.lockedInHierarchy || !track.clips.Any()) 352 continue; 353 354 var firstClip = track.clips.First(); 355 SelectionManager.SelectOnly(firstClip); 356 TimelineHelpers.FrameItems(new ITimelineItem[] { firstClip.ToItem() }); 357 358 return true; 359 } 360 361 return false; 362 } 363 364 public static bool SelectUpTrack(bool shift = false) 365 { 366 if (TrackHeadActive()) 367 { 368 var prevTrack = PreviousTrack(SelectionManager.SelectedTracks().Last()); 369 if (prevTrack != null) 370 { 371 if (shift) 372 { 373 if (SelectionManager.Contains(prevTrack)) 374 SelectionManager.Remove(SelectionManager.SelectedTracks().Last()); 375 SelectionManager.Add(prevTrack); 376 } 377 else 378 SelectionManager.SelectOnly(prevTrack); 379 FrameTrackHeader(GetVisibleTracks().First(x => x.track == prevTrack)); 380 } 381 return true; 382 } 383 return false; 384 } 385 386 public static bool SelectUpItem() 387 { 388 if (ClipAreaActive()) 389 { 390 var refItem = SelectionManager.SelectedItems().Last(); 391 var prevTrack = refItem.parentTrack.PreviousTrack(); 392 while (prevTrack != null) 393 { 394 var selectionItem = GetClosestItem(prevTrack, refItem); 395 if (selectionItem == null || prevTrack.lockedInHierarchy) 396 { 397 prevTrack = prevTrack.PreviousTrack(); 398 continue; 399 } 400 401 SelectionManager.SelectOnly(selectionItem); 402 TimelineHelpers.FrameItems(new[] { selectionItem }); 403 FrameTrackHeader(GetVisibleTracks().First(x => x.track == selectionItem.parentTrack)); 404 break; 405 } 406 return true; 407 } 408 409 return false; 410 } 411 412 public static bool SelectDownTrack(bool shift = false) 413 { 414 if (TrackHeadActive()) 415 { 416 var nextTrack = SelectionManager.SelectedTracks().Last().NextTrack(); 417 if (nextTrack != null) 418 { 419 if (shift) 420 { 421 if (SelectionManager.Contains(nextTrack)) 422 SelectionManager.Remove(SelectionManager.SelectedTracks().Last()); 423 SelectionManager.Add(nextTrack); 424 } 425 else 426 SelectionManager.SelectOnly(nextTrack); 427 428 FrameTrackHeader(GetVisibleTracks().First(x => x.track == nextTrack)); 429 } 430 return true; 431 } 432 433 return false; 434 } 435 436 public static bool SelectDownItem() 437 { 438 if (ClipAreaActive()) 439 { 440 var refItem = SelectionManager.SelectedItems().Last(); 441 var nextTrack = refItem.parentTrack.NextTrack(); 442 while (nextTrack != null) 443 { 444 var selectionItem = GetClosestItem(nextTrack, refItem); 445 if (selectionItem == null || nextTrack.lockedInHierarchy) 446 { 447 nextTrack = nextTrack.NextTrack(); 448 continue; 449 } 450 451 SelectionManager.SelectOnly(selectionItem); 452 TimelineHelpers.FrameItems(new[] { selectionItem }); 453 FrameTrackHeader(GetVisibleTracks().First(x => x.track == selectionItem.parentTrack)); 454 break; 455 } 456 return true; 457 } 458 return false; 459 } 460 } 461}