A game about forced loneliness, made by TACStudios
at master 406 lines 16 kB view raw
1using System; 2using System.Linq; 3using UnityEditor.Timeline.Actions; 4using UnityEngine; 5using UnityEngine.Timeline; 6 7namespace UnityEditor.Timeline 8{ 9 static class Gaps 10 { 11 static readonly string kInsertTime = L10n.Tr("Insert Time"); 12 13 public static void Insert(TimelineAsset asset, double at, double amount, double tolerance) 14 { 15 var tracks = asset.flattenedTracks.Where(x => x.lockedInHierarchy == false).ToList(); 16 // gather all clips 17 var clips = tracks.SelectMany(x => x.clips).Where(x => (x.start - at) >= -tolerance).ToList(); 18 var markers = tracks.SelectMany(x => x.GetMarkers()).Where(x => (x.time - at) >= -tolerance).ToList(); 19 20 // push undo on the tracks for the clips that are being modified 21 UndoExtensions.RegisterClips(clips, kInsertTime); 22 23 // push the clips 24 foreach (var clip in clips) 25 { 26 clip.start += amount; 27 } 28 29 // push undos and move the markers 30 UndoExtensions.RegisterMarkers(markers, kInsertTime); 31 foreach (var marker in markers) 32 { 33 marker.time += amount; 34 } 35 36 TimelineEditor.Refresh(RefreshReason.ContentsModified); 37 } 38 } 39 40 class PlayheadContextMenu : Manipulator 41 { 42 readonly TimeAreaItem m_TimeAreaItem; 43 static readonly int[] kFrameInsertionValues = { 5, 10, 25, 100 }; 44 static readonly GUIContent[] kFrameInsertionValuesGuiContents = 45 { 46 L10n.TextContent("Insert/Frame/5 Frames"), 47 L10n.TextContent("Insert/Frame/10 Frames"), 48 L10n.TextContent("Insert/Frame/25 Frames"), 49 L10n.TextContent("Insert/Frame/100 Frames") 50 }; 51 52 static readonly GUIContent kSingleFrameGuiContents = L10n.TextContent("Insert/Frame/Single"); 53 static readonly GUIContent kSelectedTimeGuiContents = L10n.TextContent("Insert/Selected Time"); 54 55 public PlayheadContextMenu(TimeAreaItem timeAreaItem) 56 { 57 m_TimeAreaItem = timeAreaItem; 58 } 59 60 protected override bool ContextClick(Event evt, WindowState state) 61 { 62 if (!m_TimeAreaItem.bounds.Contains(evt.mousePosition)) 63 return false; 64 65 var tolerance = TimeUtility.GetEpsilon(state.editSequence.time, state.referenceSequence.frameRate); 66 var menu = new GenericMenu(); 67 68 if (!TimelineWindow.instance.state.editSequence.isReadOnly) 69 { 70 menu.AddItem(kSingleFrameGuiContents, false, () => 71 Gaps.Insert(state.editSequence.asset, state.editSequence.time, 72 1.0 / state.referenceSequence.frameRate, tolerance) 73 ); 74 75 for (var i = 0; i != kFrameInsertionValues.Length; ++i) 76 { 77 double f = kFrameInsertionValues[i]; 78 menu.AddItem( 79 kFrameInsertionValuesGuiContents[i], 80 false, () => 81 Gaps.Insert(state.editSequence.asset, state.editSequence.time, 82 f / state.referenceSequence.frameRate, tolerance) 83 ); 84 } 85 86 var playRangeTime = state.playRange; 87 if (playRangeTime.start > playRangeTime.end) 88 { 89 menu.AddItem(kSelectedTimeGuiContents, false, () => 90 Gaps.Insert(state.editSequence.asset, playRangeTime.start, playRangeTime.end - playRangeTime.start, 91 TimeUtility.GetEpsilon(playRangeTime.start, state.referenceSequence.frameRate)) 92 ); 93 } 94 } 95 96 menu.AddItem(L10n.TextContent("Select/Clips Ending Before"), false, () => SelectClipsEndingBefore(state)); 97 menu.AddItem(L10n.TextContent("Select/Clips Starting Before"), false, () => SelectClipsStartingBefore(state)); 98 menu.AddItem(L10n.TextContent("Select/Clips Ending After"), false, () => SelectClipsEndingAfter(state)); 99 menu.AddItem(L10n.TextContent("Select/Clips Starting After"), false, () => SelectClipsStartingAfter(state)); 100 menu.AddItem(L10n.TextContent("Select/Clips Intersecting"), false, () => SelectClipsIntersecting(state)); 101 menu.AddItem(L10n.TextContent("Select/Blends Intersecting"), false, () => SelectBlendsIntersecting(state)); 102 menu.ShowAsContext(); 103 104 return true; 105 } 106 107 internal static void SelectClipsEndingBefore(WindowState state) 108 { 109 var tolerance = TimeUtility.GetEpsilon(state.editSequence.time, state.referenceSequence.frameRate); 110 SelectMenuCallback(x => x.end < state.editSequence.time + tolerance, state); 111 } 112 113 internal static void SelectClipsStartingBefore(WindowState state) 114 { 115 var tolerance = TimeUtility.GetEpsilon(state.editSequence.time, state.referenceSequence.frameRate); 116 SelectMenuCallback(x => x.start < state.editSequence.time + tolerance, state); 117 } 118 119 internal static void SelectClipsEndingAfter(WindowState state) 120 { 121 var tolerance = TimeUtility.GetEpsilon(state.editSequence.time, state.referenceSequence.frameRate); 122 SelectMenuCallback(x => x.end - state.editSequence.time >= -tolerance, state); 123 } 124 125 internal static void SelectClipsStartingAfter(WindowState state) 126 { 127 var tolerance = TimeUtility.GetEpsilon(state.editSequence.time, state.referenceSequence.frameRate); 128 SelectMenuCallback(x => x.start - state.editSequence.time >= -tolerance, state); 129 } 130 131 internal static void SelectClipsIntersecting(WindowState state) 132 { 133 SelectMenuCallback(x => x.start <= state.editSequence.time && state.editSequence.time <= x.end, state); 134 } 135 136 internal static void SelectBlendsIntersecting(WindowState state) 137 { 138 SelectMenuCallback(x => SelectBlendingIntersecting(x, state.editSequence.time), state); 139 } 140 141 static bool SelectBlendingIntersecting(TimelineClip clip, double time) 142 { 143 return clip.start <= time && time <= clip.end && ( 144 (time <= clip.start + clip.blendInDuration) || 145 (time >= clip.end - clip.blendOutDuration) 146 ); 147 } 148 149 static void SelectMenuCallback(Func<TimelineClip, bool> selector, WindowState state) 150 { 151 var allClips = state.GetWindow().treeView.allClipGuis; 152 if (allClips == null) 153 return; 154 155 SelectionManager.Clear(); 156 for (var i = 0; i != allClips.Count; ++i) 157 { 158 var c = allClips[i]; 159 160 if (c != null && c.clip != null && c.clip.GetParentTrack().lockedInHierarchy == false && selector(c.clip)) 161 { 162 SelectionManager.Add(c.clip); 163 } 164 } 165 } 166 } 167 168 class TimeAreaContextMenu : Manipulator 169 { 170 protected override bool ContextClick(Event evt, WindowState state) 171 { 172 if (state.timeAreaRect.Contains(Event.current.mousePosition)) 173 { 174 var menu = new GenericMenu(); 175 AddTimeAreaMenuItems(menu, state); 176 menu.ShowAsContext(); 177 return true; 178 } 179 return false; 180 } 181 182 internal static void AddTimeAreaMenuItems(GenericMenu menu, WindowState state) 183 { 184 foreach (var value in Enum.GetValues(typeof(TimelineAsset.DurationMode))) 185 { 186 var mode = (TimelineAsset.DurationMode)value; 187 var item = EditorGUIUtility.TextContent(string.Format(TimelineWindow.Styles.DurationModeText, L10n.Tr(ObjectNames.NicifyVariableName(mode.ToString())))); 188 189 if (state.recording || state.IsEditingASubTimeline() || state.editSequence.asset == null 190 || state.editSequence.isReadOnly) 191 menu.AddDisabledItem(item); 192 else 193 menu.AddItem(item, state.editSequence.asset.durationMode == mode, () => SelectDurationCallback(state, mode)); 194 195 menu.AddItem(DirectorStyles.showMarkersOnTimeline, state.showMarkerHeader, () => state.showMarkerHeader = !state.showMarkerHeader); 196 } 197 } 198 199 static void SelectDurationCallback(WindowState state, TimelineAsset.DurationMode mode) 200 { 201 if (mode == state.editSequence.asset.durationMode) 202 return; 203 204 UndoExtensions.RegisterTimeline(state.editSequence.asset, "Duration Mode"); 205 206 207 // if we switched from Auto to Fixed, use the auto duration as the new fixed duration so the end marker stay in the same position. 208 if (state.editSequence.asset.durationMode == TimelineAsset.DurationMode.BasedOnClips && mode == TimelineAsset.DurationMode.FixedLength) 209 { 210 state.editSequence.asset.UpdateFixedDurationWithItemsDuration(); 211 } 212 213 state.editSequence.asset.durationMode = mode; 214 state.UpdateRootPlayableDuration(state.editSequence.duration); 215 } 216 } 217 218 class Scrub : Manipulator 219 { 220 readonly Func<Event, WindowState, bool> m_OnMouseDown; 221 readonly Action<double> m_OnMouseDrag; 222 readonly System.Action m_OnMouseUp; 223 224 bool m_IsCaptured; 225 226 public Scrub(Func<Event, WindowState, bool> onMouseDown, Action<double> onMouseDrag, System.Action onMouseUp) 227 { 228 m_OnMouseDown = onMouseDown; 229 m_OnMouseDrag = onMouseDrag; 230 m_OnMouseUp = onMouseUp; 231 } 232 233 protected override bool MouseDown(Event evt, WindowState state) 234 { 235 if (evt.button != 0) 236 return false; 237 238 if (!m_OnMouseDown(evt, state)) 239 return false; 240 241 state.AddCaptured(this); 242 m_IsCaptured = true; 243 244 return true; 245 } 246 247 protected override bool MouseUp(Event evt, WindowState state) 248 { 249 if (!m_IsCaptured) 250 return false; 251 252 m_IsCaptured = false; 253 state.RemoveCaptured(this); 254 255 m_OnMouseUp(); 256 257 return true; 258 } 259 260 protected override bool MouseDrag(Event evt, WindowState state) 261 { 262 if (!m_IsCaptured) 263 return false; 264 265 m_OnMouseDrag(state.GetSnappedTimeAtMousePosition(evt.mousePosition)); 266 267 return true; 268 } 269 } 270 271 class TimeAreaItem : Control 272 { 273 public Color headColor { get; set; } 274 public Color lineColor { get; set; } 275 public bool drawLine { get; set; } 276 public bool drawHead { get; set; } 277 public bool canMoveHead { get; set; } 278 public string tooltip { get; set; } 279 public Vector2 boundOffset { get; set; } 280 281 readonly GUIContent m_HeaderContent = new GUIContent(); 282 readonly GUIStyle m_Style; 283 readonly Tooltip m_Tooltip; 284 285 Rect m_BoundingRect; 286 287 float widgetHeight { get { return m_Style.fixedHeight; } } 288 float widgetWidth { get { return m_Style.fixedWidth; } } 289 290 public Rect bounds 291 { 292 get 293 { 294 Rect r = m_BoundingRect; 295 r.y = TimelineWindow.instance.state.timeAreaRect.yMax - widgetHeight; 296 r.position += boundOffset; 297 298 return r; 299 } 300 } 301 302 public GUIStyle style 303 { 304 get { return m_Style; } 305 } 306 307 308 public bool showTooltip { get; set; } 309 310 // is this the first frame the drag callback is being invoked 311 public bool firstDrag { get; private set; } 312 313 public TimeAreaItem(GUIStyle style, Action<double> onDrag) 314 { 315 m_Style = style; 316 headColor = Color.white; 317 var scrub = new Scrub( 318 (evt, state) => 319 { 320 firstDrag = true; 321 return state.timeAreaRect.Contains(evt.mousePosition) && bounds.Contains(evt.mousePosition); 322 }, 323 (d) => 324 { 325 if (onDrag != null) 326 onDrag(d); 327 firstDrag = false; 328 }, 329 () => 330 { 331 showTooltip = false; 332 firstDrag = false; 333 } 334 ); 335 AddManipulator(scrub); 336 lineColor = m_Style.normal.textColor; 337 drawLine = true; 338 drawHead = true; 339 canMoveHead = false; 340 tooltip = string.Empty; 341 boundOffset = Vector2.zero; 342 m_Tooltip = new Tooltip(DirectorStyles.Instance.displayBackground, DirectorStyles.Instance.tinyFont); 343 } 344 345 public void Draw(Rect rect, WindowState state, double time) 346 { 347 var clipRect = new Rect(0.0f, 0.0f, TimelineWindow.instance.position.width, TimelineWindow.instance.position.height); 348 clipRect.xMin += state.sequencerHeaderWidth; 349 350 using (new GUIViewportScope(clipRect)) 351 { 352 Vector2 windowCoordinate = rect.min; 353 windowCoordinate.y += 4.0f; 354 355 windowCoordinate.x = state.TimeToPixel(time); 356 357 m_BoundingRect = new Rect((windowCoordinate.x - widgetWidth / 2.0f), windowCoordinate.y, widgetWidth, widgetHeight); 358 359 // Do not paint if the time cursor goes outside the timeline bounds... 360 if (Event.current.type == EventType.Repaint) 361 { 362 if (m_BoundingRect.xMax < state.timeAreaRect.xMin) 363 return; 364 if (m_BoundingRect.xMin > state.timeAreaRect.xMax) 365 return; 366 } 367 368 var top = new Vector3(windowCoordinate.x, rect.y - DirectorStyles.kDurationGuiThickness); 369 var bottom = new Vector3(windowCoordinate.x, rect.yMax); 370 371 if (drawLine) 372 { 373 Rect lineRect = Rect.MinMaxRect(top.x - 0.5f, top.y, bottom.x + 0.5f, bottom.y); 374 EditorGUI.DrawRect(lineRect, lineColor); 375 } 376 377 if (drawHead && Event.current.type == EventType.Repaint) 378 { 379 Color c = GUI.color; 380 GUI.color = headColor; 381 style.Draw(bounds, m_HeaderContent, false, false, false, false); 382 GUI.color = c; 383 384 if (canMoveHead) 385 EditorGUIUtility.AddCursorRect(bounds, MouseCursor.MoveArrow); 386 } 387 388 if (showTooltip) 389 { 390 m_Tooltip.text = TimeReferenceUtility.ToTimeString(time); 391 392 Vector2 position = bounds.position; 393 position.y = state.timeAreaRect.y; 394 position.y -= m_Tooltip.bounds.height; 395 position.x -= Mathf.Abs(m_Tooltip.bounds.width - bounds.width) / 2.0f; 396 397 Rect tooltipBounds = bounds; 398 tooltipBounds.position = position; 399 m_Tooltip.bounds = tooltipBounds; 400 401 m_Tooltip.Draw(); 402 } 403 } 404 } 405 } 406}