123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363 |
- using System;
- using System.Linq;
- using UnityEngine;
- using UnityEngine.Timeline;
- namespace UnityEditor.Timeline
- {
- static class Gaps
- {
- static readonly string kInsertTime = "Insert Time";
- public static void Insert(TimelineAsset asset, double at, double amount, double tolerance)
- {
- // gather all clips
- var clips = asset.flattenedTracks.SelectMany(x => x.clips).Where(x => (x.start - at) >= -tolerance).ToList();
- var markers = asset.flattenedTracks.SelectMany(x => x.GetMarkers()).Where(x => (x.time - at) >= -tolerance).ToList();
- if (!clips.Any() && !markers.Any())
- return;
- // push undo on the tracks for the clips that are being modified
- foreach (var t in clips.Select(x => x.parentTrack).Distinct())
- {
- TimelineUndo.PushUndo(t, kInsertTime);
- }
- // push the clips
- foreach (var clip in clips)
- {
- clip.start += amount;
- }
- // push undos and move the markers
- foreach (var marker in markers)
- {
- var obj = marker as UnityEngine.Object;
- if (obj != null)
- TimelineUndo.PushUndo(obj, kInsertTime);
- marker.time += amount;
- }
- TimelineEditor.Refresh(RefreshReason.ContentsModified);
- }
- }
- class PlayheadContextMenu : Manipulator
- {
- readonly TimeAreaItem m_TimeAreaItem;
- static readonly int[] kFrameInsertionValues = {5, 10, 25, 100};
- public PlayheadContextMenu(TimeAreaItem timeAreaItem)
- {
- m_TimeAreaItem = timeAreaItem;
- }
- protected override bool ContextClick(Event evt, WindowState state)
- {
- if (!m_TimeAreaItem.bounds.Contains(evt.mousePosition))
- return false;
- var tolerance = TimeUtility.GetEpsilon(state.editSequence.time, state.referenceSequence.frameRate);
- var menu = new GenericMenu();
- if (!TimelineWindow.instance.state.editSequence.isReadOnly)
- {
- menu.AddItem(EditorGUIUtility.TrTextContent("Insert/Frame/Single"), false, () =>
- Gaps.Insert(state.editSequence.asset, state.editSequence.time, 1.0 / state.referenceSequence.frameRate, tolerance)
- );
- for (var i = 0; i != kFrameInsertionValues.Length; ++i)
- {
- double f = kFrameInsertionValues[i];
- menu.AddItem(EditorGUIUtility.TrTextContent("Insert/Frame/" + kFrameInsertionValues[i] + " Frames"), false, () =>
- Gaps.Insert(state.editSequence.asset, state.editSequence.time, f / state.referenceSequence.frameRate, tolerance)
- );
- }
- var playRangeTime = state.playRange;
- if (playRangeTime.y > playRangeTime.x)
- {
- menu.AddItem(EditorGUIUtility.TrTextContent("Insert/Selected Time"), false, () =>
- Gaps.Insert(state.editSequence.asset, playRangeTime.x, playRangeTime.y - playRangeTime.x, TimeUtility.GetEpsilon(playRangeTime.x, state.referenceSequence.frameRate))
- );
- }
- }
- menu.AddItem(EditorGUIUtility.TrTextContent("Select/Clips Ending Before"), false, () => SelectMenuCallback(x => x.end < state.editSequence.time + tolerance, state));
- menu.AddItem(EditorGUIUtility.TrTextContent("Select/Clips Starting Before"), false, () => SelectMenuCallback(x => x.start < state.editSequence.time + tolerance, state));
- menu.AddItem(EditorGUIUtility.TrTextContent("Select/Clips Ending After"), false, () => SelectMenuCallback(x => x.end - state.editSequence.time >= -tolerance, state));
- menu.AddItem(EditorGUIUtility.TrTextContent("Select/Clips Starting After"), false, () => SelectMenuCallback(x => x.start - state.editSequence.time >= -tolerance, state));
- menu.AddItem(EditorGUIUtility.TrTextContent("Select/Clips Intersecting"), false, () => SelectMenuCallback(x => x.start <= state.editSequence.time && state.editSequence.time <= x.end, state));
- menu.AddItem(EditorGUIUtility.TrTextContent("Select/Blends Intersecting"), false, () => SelectMenuCallback(x => SelectBlendingIntersecting(x, state.editSequence.time), state));
- menu.ShowAsContext();
- return true;
- }
- static bool SelectBlendingIntersecting(TimelineClip clip, double time)
- {
- return clip.start <= time && time <= clip.end && (
- (time <= clip.start + clip.blendInDuration) ||
- (time >= clip.end - clip.blendOutDuration)
- );
- }
- static void SelectMenuCallback(Func<TimelineClip, bool> selector, WindowState state)
- {
- var allClips = state.GetWindow().treeView.allClipGuis;
- if (allClips == null)
- return;
- SelectionManager.Clear();
- for (var i = 0; i != allClips.Count; ++i)
- {
- var c = allClips[i];
- if (c != null && c.clip != null && selector(c.clip))
- {
- SelectionManager.Add(c.clip);
- }
- }
- }
- }
- class TimeAreaContextMenu : Manipulator
- {
- protected override bool ContextClick(Event evt, WindowState state)
- {
- if (state.timeAreaRect.Contains(Event.current.mousePosition))
- {
- var menu = new GenericMenu();
- AddTimeAreaMenuItems(menu, state);
- menu.ShowAsContext();
- return true;
- }
- return false;
- }
- internal static void AddTimeAreaMenuItems(GenericMenu menu, WindowState state)
- {
- foreach (var value in Enum.GetValues(typeof(TimelineAsset.DurationMode)))
- {
- var mode = (TimelineAsset.DurationMode)value;
- var item = EditorGUIUtility.TextContent(string.Format(TimelineWindow.Styles.DurationModeText, L10n.Tr(ObjectNames.NicifyVariableName(mode.ToString()))));
- if (state.recording || state.IsEditingASubTimeline() || state.editSequence.asset == null
- || state.editSequence.isReadOnly)
- menu.AddDisabledItem(item);
- else
- menu.AddItem(item, state.editSequence.asset.durationMode == mode, () => SelectDurationCallback(state, mode));
- menu.AddItem(DirectorStyles.showMarkersOnTimeline, state.showMarkerHeader, () => new ToggleShowMarkersOnTimeline().Execute(state));
- }
- }
- static void SelectDurationCallback(WindowState state, TimelineAsset.DurationMode mode)
- {
- if (mode == state.editSequence.asset.durationMode)
- return;
- TimelineUndo.PushUndo(state.editSequence.asset, "Duration Mode");
- // 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.
- if (state.editSequence.asset.durationMode == TimelineAsset.DurationMode.BasedOnClips && mode == TimelineAsset.DurationMode.FixedLength)
- {
- state.editSequence.asset.fixedDuration = state.editSequence.duration;
- }
- state.editSequence.asset.durationMode = mode;
- state.UpdateRootPlayableDuration(state.editSequence.duration);
- }
- }
- class Scrub : Manipulator
- {
- readonly Func<Event, WindowState, bool> m_OnMouseDown;
- readonly Action<double> m_OnMouseDrag;
- readonly Action m_OnMouseUp;
- bool m_IsCaptured;
- public Scrub(Func<Event, WindowState, bool> onMouseDown, Action<double> onMouseDrag, Action onMouseUp)
- {
- m_OnMouseDown = onMouseDown;
- m_OnMouseDrag = onMouseDrag;
- m_OnMouseUp = onMouseUp;
- }
- protected override bool MouseDown(Event evt, WindowState state)
- {
- if (evt.button != 0)
- return false;
- if (!m_OnMouseDown(evt, state))
- return false;
- state.AddCaptured(this);
- m_IsCaptured = true;
- return true;
- }
- protected override bool MouseUp(Event evt, WindowState state)
- {
- if (!m_IsCaptured)
- return false;
- m_IsCaptured = false;
- state.RemoveCaptured(this);
- m_OnMouseUp();
- return true;
- }
- protected override bool MouseDrag(Event evt, WindowState state)
- {
- if (!m_IsCaptured)
- return false;
- m_OnMouseDrag(state.GetSnappedTimeAtMousePosition(evt.mousePosition));
- return true;
- }
- }
- class TimeAreaItem : Control
- {
- public Color headColor { get; set; }
- public Color lineColor { get; set; }
- public bool drawLine { get; set; }
- public bool drawHead { get; set; }
- public bool canMoveHead { get; set; }
- public string tooltip { get; set; }
- public Vector2 boundOffset { get; set; }
- readonly GUIContent m_HeaderContent = new GUIContent();
- readonly GUIStyle m_Style;
- readonly Tooltip m_Tooltip;
- Rect m_BoundingRect;
- float widgetHeight { get { return m_Style.fixedHeight; } }
- float widgetWidth { get { return m_Style.fixedWidth; } }
- public Rect bounds
- {
- get
- {
- Rect r = m_BoundingRect;
- r.y = TimelineWindow.instance.state.timeAreaRect.yMax - widgetHeight;
- r.position += boundOffset;
- return r;
- }
- }
- public GUIStyle style
- {
- get { return m_Style; }
- }
- public bool showTooltip { get; set; }
- // is this the first frame the drag callback is being invoked
- public bool firstDrag { get; private set; }
- public TimeAreaItem(GUIStyle style, Action<double> onDrag)
- {
- m_Style = style;
- headColor = Color.white;
- var scrub = new Scrub(
- (evt, state) =>
- {
- firstDrag = true;
- return state.timeAreaRect.Contains(evt.mousePosition) && bounds.Contains(evt.mousePosition);
- },
- (d) =>
- {
- if (onDrag != null)
- onDrag(d);
- firstDrag = false;
- },
- () =>
- {
- showTooltip = false;
- firstDrag = false;
- }
- );
- AddManipulator(scrub);
- lineColor = m_Style.normal.textColor;
- drawLine = true;
- drawHead = true;
- canMoveHead = false;
- tooltip = string.Empty;
- boundOffset = Vector2.zero;
- m_Tooltip = new Tooltip(DirectorStyles.Instance.displayBackground, DirectorStyles.Instance.tinyFont);
- }
- public void Draw(Rect rect, WindowState state, double time)
- {
- var clipRect = new Rect(0.0f, 0.0f, TimelineWindow.instance.position.width, TimelineWindow.instance.position.height);
- clipRect.xMin += state.sequencerHeaderWidth;
- using (new GUIViewportScope(clipRect))
- {
- Vector2 windowCoordinate = rect.min;
- windowCoordinate.y += 4.0f;
- windowCoordinate.x = state.TimeToPixel(time);
- m_BoundingRect = new Rect((windowCoordinate.x - widgetWidth / 2.0f), windowCoordinate.y, widgetWidth, widgetHeight);
- // Do not paint if the time cursor goes outside the timeline bounds...
- if (Event.current.type == EventType.Repaint)
- {
- if (m_BoundingRect.xMax < state.timeAreaRect.xMin)
- return;
- if (m_BoundingRect.xMin > state.timeAreaRect.xMax)
- return;
- }
- var top = new Vector3(windowCoordinate.x, rect.y - DirectorStyles.kDurationGuiThickness);
- var bottom = new Vector3(windowCoordinate.x, rect.yMax);
- if (drawLine)
- {
- Rect lineRect = Rect.MinMaxRect(top.x - 0.5f, top.y, bottom.x + 0.5f, bottom.y);
- EditorGUI.DrawRect(lineRect, lineColor);
- }
- if (drawHead)
- {
- Color c = GUI.color;
- GUI.color = headColor;
- GUI.Box(bounds, m_HeaderContent, m_Style);
- GUI.color = c;
- if (canMoveHead)
- EditorGUIUtility.AddCursorRect(bounds, MouseCursor.MoveArrow);
- }
- if (showTooltip)
- {
- m_Tooltip.text = TimeReferenceUtility.ToTimeString(time);
- Vector2 position = bounds.position;
- position.y = state.timeAreaRect.y;
- position.y -= m_Tooltip.bounds.height;
- position.x -= Mathf.Abs(m_Tooltip.bounds.width - bounds.width) / 2.0f;
- Rect tooltipBounds = bounds;
- tooltipBounds.position = position;
- m_Tooltip.bounds = tooltipBounds;
- m_Tooltip.Draw();
- }
- }
- }
- }
- }
|