Maze / Library /PackageCache /com.unity.timeline@1.6.5 /Editor /treeview /ItemGui /TimelineClipGUI.cs
| using System; | |
| using System.Collections.Generic; | |
| using System.Linq; | |
| using UnityEngine; | |
| using UnityEngine.Playables; | |
| using UnityEngine.Timeline; | |
| namespace UnityEditor.Timeline | |
| { | |
| class TimelineClipGUI : TimelineItemGUI, IClipCurveEditorOwner, ISnappable, IAttractable | |
| { | |
| EditorClip m_EditorItem; | |
| Rect m_ClipCenterSection; | |
| readonly List<Rect> m_LoopRects = new List<Rect>(); | |
| ClipDrawData m_ClipDrawData; | |
| Rect m_MixOutRect; | |
| Rect m_MixInRect; | |
| int m_MinLoopIndex = 1; | |
| // clip dirty detection | |
| int m_LastDirtyIndex = Int32.MinValue; | |
| bool m_ClipViewDirty = true; | |
| bool supportResize { get; } | |
| public ClipCurveEditor clipCurveEditor { get; set; } | |
| public TimelineClipGUI previousClip { get; set; } | |
| public TimelineClipGUI nextClip { get; set; } | |
| static readonly float k_MinMixWidth = 2; | |
| static readonly float k_MaxHandleWidth = 10f; | |
| static readonly float k_MinHandleWidth = 1f; | |
| bool? m_ShowDrillIcon; | |
| ClipEditor m_ClipEditor; | |
| static List<PlayableDirector> s_TempSubDirectors = new List<PlayableDirector>(); | |
| static readonly IconData k_DiggableClipIcon = new IconData(DirectorStyles.LoadIcon("TimelineDigIn")); | |
| string name | |
| { | |
| get | |
| { | |
| if (string.IsNullOrEmpty(clip.displayName)) | |
| return "(Empty)"; | |
| return clip.displayName; | |
| } | |
| } | |
| public bool inlineCurvesSelected => SelectionManager.IsCurveEditorFocused(this); | |
| public Rect mixOutRect | |
| { | |
| get | |
| { | |
| var percent = clip.mixOutPercentage; | |
| var x = Mathf.Round(treeViewRect.width * (1 - percent)); | |
| var width = Mathf.Round(treeViewRect.width * percent); | |
| m_MixOutRect.Set(x, 0.0f, width, treeViewRect.height); | |
| return m_MixOutRect; | |
| } | |
| } | |
| public Rect mixInRect | |
| { | |
| get | |
| { | |
| var width = Mathf.Round(treeViewRect.width * clip.mixInPercentage); | |
| m_MixInRect.Set(0.0f, 0.0f, width, treeViewRect.height); | |
| return m_MixInRect; | |
| } | |
| } | |
| public ClipBlends GetClipBlends() | |
| { | |
| var _mixInRect = mixInRect; | |
| var _mixOutRect = mixOutRect; | |
| var blendInKind = BlendKind.None; | |
| if (_mixInRect.width > k_MinMixWidth && clip.hasBlendIn) | |
| blendInKind = BlendKind.Mix; | |
| else if (_mixInRect.width > k_MinMixWidth) | |
| blendInKind = BlendKind.Ease; | |
| var blendOutKind = BlendKind.None; | |
| if (_mixOutRect.width > k_MinMixWidth && clip.hasBlendOut) | |
| blendOutKind = BlendKind.Mix; | |
| else if (_mixOutRect.width > k_MinMixWidth) | |
| blendOutKind = BlendKind.Ease; | |
| return new ClipBlends(blendInKind, _mixInRect, blendOutKind, _mixOutRect); | |
| } | |
| public override double start | |
| { | |
| get { return clip.start; } | |
| } | |
| public override double end | |
| { | |
| get { return clip.end; } | |
| } | |
| public bool supportsLooping | |
| { | |
| get { return clip.SupportsLooping(); } | |
| } | |
| // for the inline curve editor, only show loops if we recorded the asset | |
| bool IClipCurveEditorOwner.showLoops | |
| { | |
| get { return clip.SupportsLooping() && (clip.asset is AnimationPlayableAsset); } | |
| } | |
| TrackAsset IClipCurveEditorOwner.owner | |
| { | |
| get { return clip.GetParentTrack(); } | |
| } | |
| public bool supportsSubTimelines | |
| { | |
| get { return m_ClipEditor.supportsSubTimelines; } | |
| } | |
| public int minLoopIndex | |
| { | |
| get { return m_MinLoopIndex; } | |
| } | |
| public Rect clippedRect { get; private set; } | |
| public override void Select() | |
| { | |
| MoveToTop(); | |
| SelectionManager.Add(clip); | |
| if (clipCurveEditor != null && SelectionManager.Count() == 1) | |
| SelectionManager.SelectInlineCurveEditor(this); | |
| } | |
| public override bool IsSelected() | |
| { | |
| return SelectionManager.Contains(clip); | |
| } | |
| public override void Deselect() | |
| { | |
| SelectionManager.Remove(clip); | |
| if (inlineCurvesSelected) | |
| SelectionManager.SelectInlineCurveEditor(null); | |
| } | |
| public override bool CanSelect(Event evt) | |
| { | |
| ClipBlends clipBlends = GetClipBlends(); | |
| Vector2 mousePos = evt.mousePosition - rect.position; | |
| return m_ClipCenterSection.Contains(mousePos) || IsPointLocatedInClipBlend(mousePos, clipBlends); | |
| } | |
| static bool IsPointLocatedInClipBlend(Vector2 pt, ClipBlends blends) | |
| { | |
| if (blends.inRect.Contains(pt)) | |
| { | |
| if (blends.inKind == BlendKind.Mix) | |
| return Sign(pt, blends.inRect.min, blends.inRect.max) < 0; | |
| return true; | |
| } | |
| if (blends.outRect.Contains(pt)) | |
| { | |
| if (blends.outKind == BlendKind.Mix) | |
| return Sign(pt, blends.outRect.min, blends.outRect.max) >= 0; | |
| return true; | |
| } | |
| return false; | |
| } | |
| static float Sign(Vector2 point, Vector2 linePoint1, Vector2 linePoint2) | |
| { | |
| return (point.x - linePoint2.x) * (linePoint1.y - linePoint2.y) - (linePoint1.x - linePoint2.x) * (point.y - linePoint2.y); | |
| } | |
| public override ITimelineItem item | |
| { | |
| get { return ItemsUtils.ToItem(clip); } | |
| } | |
| IZOrderProvider zOrderProvider { get; } | |
| public TimelineClipHandle leftHandle { get; } | |
| public TimelineClipHandle rightHandle { get; } | |
| public TimelineClipGUI(TimelineClip clip, IRowGUI parent, IZOrderProvider provider) : base(parent) | |
| { | |
| zOrderProvider = provider; | |
| zOrder = provider.Next(); | |
| m_EditorItem = EditorClipFactory.GetEditorClip(clip); | |
| m_ClipEditor = CustomTimelineEditorCache.GetClipEditor(clip); | |
| supportResize = true; | |
| leftHandle = new TimelineClipHandle(this, TrimEdge.Start); | |
| rightHandle = new TimelineClipHandle(this, TrimEdge.End); | |
| ItemToItemGui.Add(clip, this); | |
| } | |
| void CreateInlineCurveEditor(WindowState state) | |
| { | |
| if (clipCurveEditor != null) | |
| return; | |
| var animationClip = clip.animationClip; | |
| if (animationClip != null && animationClip.empty) | |
| animationClip = null; | |
| // prune out clips coming from FBX | |
| if (animationClip != null && !clip.recordable) | |
| return; // don't show, even if there are curves | |
| if (animationClip == null && !clip.HasAnyAnimatableParameters()) | |
| return; // nothing to show | |
| state.AddEndFrameDelegate((istate, currentEvent) => | |
| { | |
| clipCurveEditor = new ClipCurveEditor(CurveDataSource.Create(this), TimelineWindow.instance, clip.GetParentTrack()); | |
| return true; | |
| }); | |
| } | |
| public TimelineClip clip | |
| { | |
| get { return m_EditorItem.clip; } | |
| } | |
| // Draw the actual clip. Defers to the track drawer for customization | |
| void UpdateDrawData(WindowState state, Rect drawRect, string title, bool selected, bool previousClipSelected, float rectXOffset) | |
| { | |
| m_ClipDrawData.clip = clip; | |
| m_ClipDrawData.targetRect = drawRect; | |
| m_ClipDrawData.clipCenterSection = m_ClipCenterSection; | |
| m_ClipDrawData.unclippedRect = treeViewRect; | |
| m_ClipDrawData.title = title; | |
| m_ClipDrawData.selected = selected; | |
| m_ClipDrawData.inlineCurvesSelected = inlineCurvesSelected; | |
| m_ClipDrawData.previousClip = previousClip != null ? previousClip.clip : null; | |
| m_ClipDrawData.previousClipSelected = previousClipSelected; | |
| Vector3 shownAreaTime = state.timeAreaShownRange; | |
| m_ClipDrawData.localVisibleStartTime = clip.ToLocalTimeUnbound(Math.Max(clip.start, shownAreaTime.x)); | |
| m_ClipDrawData.localVisibleEndTime = clip.ToLocalTimeUnbound(Math.Min(clip.end, shownAreaTime.y)); | |
| m_ClipDrawData.clippedRect = new Rect(clippedRect.x - rectXOffset, 0.0f, clippedRect.width, clippedRect.height); | |
| m_ClipDrawData.minLoopIndex = minLoopIndex; | |
| m_ClipDrawData.loopRects = m_LoopRects; | |
| m_ClipDrawData.supportsLooping = supportsLooping; | |
| m_ClipDrawData.clipBlends = GetClipBlends(); | |
| m_ClipDrawData.clipEditor = m_ClipEditor; | |
| m_ClipDrawData.ClipDrawOptions = UpdateClipDrawOptions(m_ClipEditor, clip); | |
| UpdateClipIcons(state); | |
| } | |
| void UpdateClipIcons(WindowState state) | |
| { | |
| // Pass 1 - gather size | |
| int required = 0; | |
| bool requiresDigIn = ShowDrillIcon(state.editSequence.director); | |
| if (requiresDigIn) | |
| required++; | |
| var icons = m_ClipDrawData.ClipDrawOptions.icons; | |
| foreach (var icon in icons) | |
| { | |
| if (icon != null) | |
| required++; | |
| } | |
| // Pass 2 - copy icon data | |
| if (required == 0) | |
| { | |
| m_ClipDrawData.rightIcons = null; | |
| return; | |
| } | |
| if (m_ClipDrawData.rightIcons == null || m_ClipDrawData.rightIcons.Length != required) | |
| m_ClipDrawData.rightIcons = new IconData[required]; | |
| int index = 0; | |
| if (requiresDigIn) | |
| m_ClipDrawData.rightIcons[index++] = k_DiggableClipIcon; | |
| foreach (var icon in icons) | |
| { | |
| if (icon != null) | |
| m_ClipDrawData.rightIcons[index++] = new IconData(icon); | |
| } | |
| } | |
| static ClipDrawOptions UpdateClipDrawOptions(ClipEditor clipEditor, TimelineClip clip) | |
| { | |
| try | |
| { | |
| return clipEditor.GetClipOptions(clip); | |
| } | |
| catch (Exception e) | |
| { | |
| Debug.LogException(e); | |
| } | |
| return CustomTimelineEditorCache.GetDefaultClipEditor().GetClipOptions(clip); | |
| } | |
| static void DrawClip(ClipDrawData drawData) | |
| { | |
| ClipDrawer.DrawDefaultClip(drawData); | |
| if (drawData.clip.asset is AnimationPlayableAsset) | |
| { | |
| var state = TimelineWindow.instance.state; | |
| if (state.recording && state.IsArmedForRecord(drawData.clip.GetParentTrack())) | |
| { | |
| ClipDrawer.DrawAnimationRecordBorder(drawData); | |
| ClipDrawer.DrawRecordProhibited(drawData); | |
| } | |
| } | |
| } | |
| public void DrawGhostClip(Rect targetRect) | |
| { | |
| DrawSimpleClip(targetRect, ClipBorder.Selection(), new Color(1.0f, 1.0f, 1.0f, 0.5f)); | |
| } | |
| public void DrawInvalidClip(Rect targetRect) | |
| { | |
| DrawSimpleClip(targetRect, ClipBorder.Selection(), DirectorStyles.Instance.customSkin.colorInvalidClipOverlay); | |
| } | |
| void DrawSimpleClip(Rect targetRect, ClipBorder border, Color overlay) | |
| { | |
| var drawOptions = UpdateClipDrawOptions(CustomTimelineEditorCache.GetClipEditor(clip), clip); | |
| ClipDrawer.DrawSimpleClip(clip, targetRect, border, overlay, drawOptions); | |
| } | |
| void DrawInto(Rect drawRect, WindowState state) | |
| { | |
| if (Event.current.type != EventType.Repaint) | |
| return; | |
| // create the inline curve editor if not already created | |
| CreateInlineCurveEditor(state); | |
| // @todo optimization, most of the calculations (rect, offsets, colors, etc.) could be cached | |
| // and rebuilt when the hash of the clip changes. | |
| if (isInvalid) | |
| { | |
| DrawInvalidClip(treeViewRect); | |
| return; | |
| } | |
| GUI.BeginClip(drawRect); | |
| var originRect = new Rect(0.0f, 0.0f, drawRect.width, drawRect.height); | |
| string clipLabel = name; | |
| var selected = SelectionManager.Contains(clip); | |
| var previousClipSelected = previousClip != null && SelectionManager.Contains(previousClip.clip); | |
| if (selected && 1.0 != clip.timeScale) | |
| clipLabel += " " + clip.timeScale.ToString("F2") + "x"; | |
| UpdateDrawData(state, originRect, clipLabel, selected, previousClipSelected, drawRect.x); | |
| DrawClip(m_ClipDrawData); | |
| GUI.EndClip(); | |
| if (clip.GetParentTrack() != null && !clip.GetParentTrack().lockedInHierarchy) | |
| { | |
| if (selected && supportResize) | |
| { | |
| var cursorRect = rect; | |
| cursorRect.xMin += leftHandle.boundingRect.width; | |
| cursorRect.xMax -= rightHandle.boundingRect.width; | |
| EditorGUIUtility.AddCursorRect(cursorRect, MouseCursor.MoveArrow); | |
| } | |
| if (supportResize) | |
| { | |
| var handleWidth = Mathf.Clamp(drawRect.width * 0.3f, k_MinHandleWidth, k_MaxHandleWidth); | |
| leftHandle.Draw(drawRect, handleWidth, state); | |
| rightHandle.Draw(drawRect, handleWidth, state); | |
| } | |
| } | |
| } | |
| void CalculateClipRectangle(Rect trackRect, WindowState state) | |
| { | |
| if (m_ClipViewDirty) | |
| { | |
| var clipRect = RectToTimeline(trackRect, state); | |
| treeViewRect = clipRect; | |
| // calculate clipped rect | |
| clipRect.xMin = Mathf.Max(clipRect.xMin, trackRect.xMin); | |
| clipRect.xMax = Mathf.Min(clipRect.xMax, trackRect.xMax); | |
| if (clipRect.width > 0 && clipRect.width < 2) | |
| { | |
| clipRect.width = 5.0f; | |
| } | |
| clippedRect = clipRect; | |
| } | |
| } | |
| void AddToSpacePartitioner(WindowState state) | |
| { | |
| if (Event.current.type == EventType.Repaint && !parent.locked) | |
| state.spacePartitioner.AddBounds(this, rect); | |
| } | |
| void CalculateBlendRect() | |
| { | |
| m_ClipCenterSection = treeViewRect; | |
| m_ClipCenterSection.x = 0; | |
| m_ClipCenterSection.y = 0; | |
| m_ClipCenterSection.xMin = mixInRect.xMax; | |
| m_ClipCenterSection.width = Mathf.Round(treeViewRect.width - mixInRect.width - mixOutRect.width); | |
| m_ClipCenterSection.xMax = m_ClipCenterSection.xMin + m_ClipCenterSection.width; | |
| } | |
| // Entry point to the Clip Drawing... | |
| public override void Draw(Rect trackRect, bool trackRectChanged, WindowState state) | |
| { | |
| // if the clip has changed, fire the appropriate callback | |
| DetectClipChanged(trackRectChanged); | |
| // update the clip projected rectangle on the timeline | |
| CalculateClipRectangle(trackRect, state); | |
| AddToSpacePartitioner(state); | |
| // update the blend rects (when clip overlaps with others) | |
| CalculateBlendRect(); | |
| // update the loop rects (when clip loops) | |
| CalculateLoopRects(trackRect, state); | |
| DrawExtrapolation(trackRect, treeViewRect); | |
| DrawInto(treeViewRect, state); | |
| ResetClipChanged(); | |
| } | |
| void DetectClipChanged(bool trackRectChanged) | |
| { | |
| if (Event.current.type == EventType.Layout) | |
| { | |
| if (clip.DirtyIndex != m_LastDirtyIndex) | |
| { | |
| m_ClipViewDirty = true; | |
| try | |
| { | |
| m_ClipEditor.OnClipChanged(clip); | |
| } | |
| catch (Exception e) | |
| { | |
| Debug.LogException(e); | |
| } | |
| m_LastDirtyIndex = clip.DirtyIndex; | |
| } | |
| m_ClipViewDirty |= trackRectChanged; | |
| } | |
| } | |
| void ResetClipChanged() | |
| { | |
| if (Event.current.type == EventType.Repaint) | |
| m_ClipViewDirty = false; | |
| } | |
| internal void MoveToTop() | |
| { | |
| zOrder = zOrderProvider.Next(); | |
| } | |
| GUIStyle GetExtrapolationIcon(TimelineClip.ClipExtrapolation mode) | |
| { | |
| GUIStyle extrapolationIcon = null; | |
| switch (mode) | |
| { | |
| case TimelineClip.ClipExtrapolation.None: return null; | |
| case TimelineClip.ClipExtrapolation.Hold: extrapolationIcon = m_Styles.extrapolationHold; break; | |
| case TimelineClip.ClipExtrapolation.Loop: extrapolationIcon = m_Styles.extrapolationLoop; break; | |
| case TimelineClip.ClipExtrapolation.PingPong: extrapolationIcon = m_Styles.extrapolationPingPong; break; | |
| case TimelineClip.ClipExtrapolation.Continue: extrapolationIcon = m_Styles.extrapolationContinue; break; | |
| } | |
| return extrapolationIcon; | |
| } | |
| Rect GetPreExtrapolationBounds(Rect trackRect, Rect clipRect, GUIStyle icon) | |
| { | |
| float x = clipRect.xMin - (icon.fixedWidth + 10.0f); | |
| float y = trackRect.yMin + (trackRect.height - icon.fixedHeight) / 2.0f; | |
| if (previousClip != null) | |
| { | |
| float distance = Mathf.Abs(treeViewRect.xMin - previousClip.treeViewRect.xMax); | |
| if (distance < icon.fixedWidth) | |
| return new Rect(0.0f, 0.0f, 0.0f, 0.0f); | |
| if (distance < icon.fixedWidth + 20.0f) | |
| { | |
| float delta = (distance - icon.fixedWidth) / 2.0f; | |
| x = clipRect.xMin - (icon.fixedWidth + delta); | |
| } | |
| } | |
| return new Rect(x, y, icon.fixedWidth, icon.fixedHeight); | |
| } | |
| Rect GetPostExtrapolationBounds(Rect trackRect, Rect clipRect, GUIStyle icon) | |
| { | |
| float x = clipRect.xMax + 10.0f; | |
| float y = trackRect.yMin + (trackRect.height - icon.fixedHeight) / 2.0f; | |
| if (nextClip != null) | |
| { | |
| float distance = Mathf.Abs(nextClip.treeViewRect.xMin - treeViewRect.xMax); | |
| if (distance < icon.fixedWidth) | |
| return new Rect(0.0f, 0.0f, 0.0f, 0.0f); | |
| if (distance < icon.fixedWidth + 20.0f) | |
| { | |
| float delta = (distance - icon.fixedWidth) / 2.0f; | |
| x = clipRect.xMax + delta; | |
| } | |
| } | |
| return new Rect(x, y, icon.fixedWidth, icon.fixedHeight); | |
| } | |
| static void DrawExtrapolationIcon(Rect rect, GUIStyle icon) | |
| { | |
| GUI.Label(rect, GUIContent.none, icon); | |
| } | |
| void DrawExtrapolation(Rect trackRect, Rect clipRect) | |
| { | |
| if (clip.hasPreExtrapolation) | |
| { | |
| GUIStyle icon = GetExtrapolationIcon(clip.preExtrapolationMode); | |
| if (icon != null) | |
| { | |
| Rect iconBounds = GetPreExtrapolationBounds(trackRect, clipRect, icon); | |
| if (iconBounds.width > 1 && iconBounds.height > 1) | |
| DrawExtrapolationIcon(iconBounds, icon); | |
| } | |
| } | |
| if (clip.hasPostExtrapolation) | |
| { | |
| GUIStyle icon = GetExtrapolationIcon(clip.postExtrapolationMode); | |
| if (icon != null) | |
| { | |
| Rect iconBounds = GetPostExtrapolationBounds(trackRect, clipRect, icon); | |
| if (iconBounds.width > 1 && iconBounds.height > 1) | |
| DrawExtrapolationIcon(iconBounds, icon); | |
| } | |
| } | |
| } | |
| static Rect ProjectRectOnTimeline(Rect rect, Rect trackRect, WindowState state) | |
| { | |
| Rect newRect = rect; | |
| // transform clipRect into pixel-space | |
| newRect.x *= state.timeAreaScale.x; | |
| newRect.width *= state.timeAreaScale.x; | |
| newRect.x += state.timeAreaTranslation.x + trackRect.xMin; | |
| // adjust clipRect height and vertical centering | |
| const int clipPadding = 2; | |
| newRect.y = trackRect.y + clipPadding; | |
| newRect.height = trackRect.height - (2 * clipPadding); | |
| return newRect; | |
| } | |
| void CalculateLoopRects(Rect trackRect, WindowState state) | |
| { | |
| if (!m_ClipViewDirty) | |
| return; | |
| m_LoopRects.Clear(); | |
| if (clip.duration < WindowState.kTimeEpsilon) | |
| return; | |
| var times = TimelineHelpers.GetLoopTimes(clip); | |
| var loopDuration = TimelineHelpers.GetLoopDuration(clip); | |
| m_MinLoopIndex = -1; | |
| // we have a hold, no need to compute all loops | |
| if (!supportsLooping) | |
| { | |
| if (times.Length > 1) | |
| { | |
| var t = times[1]; | |
| float loopTime = (float)(clip.duration - t); | |
| m_LoopRects.Add(ProjectRectOnTimeline(new Rect((float)(t + clip.start), 0, loopTime, 0), trackRect, state)); | |
| } | |
| return; | |
| } | |
| var range = state.timeAreaShownRange; | |
| var visibleStartTime = range.x - clip.start; | |
| var visibleEndTime = range.y - clip.start; | |
| for (int i = 1; i < times.Length; i++) | |
| { | |
| var t = times[i]; | |
| // don't draw off screen loops | |
| if (t > visibleEndTime) | |
| break; | |
| float loopTime = Mathf.Min((float)(clip.duration - t), (float)loopDuration); | |
| var loopEnd = t + loopTime; | |
| if (loopEnd < visibleStartTime) | |
| continue; | |
| m_LoopRects.Add(ProjectRectOnTimeline(new Rect((float)(t + clip.start), 0, loopTime, 0), trackRect, state)); | |
| if (m_MinLoopIndex == -1) | |
| m_MinLoopIndex = i; | |
| } | |
| } | |
| public override Rect RectToTimeline(Rect trackRect, WindowState state) | |
| { | |
| var offsetFromTimeSpaceToPixelSpace = state.timeAreaTranslation.x + trackRect.xMin; | |
| var start = (float)(DiscreteTime)clip.start; | |
| var end = (float)(DiscreteTime)clip.end; | |
| return Rect.MinMaxRect( | |
| Mathf.Round(start * state.timeAreaScale.x + offsetFromTimeSpaceToPixelSpace), Mathf.Round(trackRect.yMin), | |
| Mathf.Round(end * state.timeAreaScale.x + offsetFromTimeSpaceToPixelSpace), Mathf.Round(trackRect.yMax) | |
| ); | |
| } | |
| public IEnumerable<Edge> SnappableEdgesFor(IAttractable attractable, ManipulateEdges manipulateEdges) | |
| { | |
| var edges = new List<Edge>(); | |
| bool canAddEdges = !parent.muted; | |
| if (canAddEdges) | |
| { | |
| // Hack: Trim Start in Ripple mode should not have any snap point added | |
| if (EditMode.editType == EditMode.EditType.Ripple && manipulateEdges == ManipulateEdges.Left) | |
| return edges; | |
| if (attractable != this) | |
| { | |
| if (EditMode.editType == EditMode.EditType.Ripple) | |
| { | |
| bool skip = false; | |
| // Hack: Since Trim End and Move in Ripple mode causes other snap point to move on the same track (which is not supported), disable snapping for this special cases... | |
| // TODO Find a proper way to have different snap edges for each edit mode. | |
| if (manipulateEdges == ManipulateEdges.Right) | |
| { | |
| var otherClipGUI = attractable as TimelineClipGUI; | |
| skip = otherClipGUI != null && otherClipGUI.parent == parent; | |
| } | |
| else if (manipulateEdges == ManipulateEdges.Both) | |
| { | |
| var moveHandler = attractable as MoveItemHandler; | |
| skip = moveHandler != null && moveHandler.movingItems.Any(clips => clips.targetTrack == clip.GetParentTrack() && clip.start >= clips.start); | |
| } | |
| if (skip) | |
| return edges; | |
| } | |
| AddEdge(edges, clip.start); | |
| AddEdge(edges, clip.end); | |
| } | |
| else | |
| { | |
| if (manipulateEdges == ManipulateEdges.Right) | |
| { | |
| var d = TimelineHelpers.GetClipAssetEndTime(clip); | |
| if (d < double.MaxValue) | |
| { | |
| if (clip.SupportsLooping()) | |
| { | |
| var l = TimelineHelpers.GetLoopDuration(clip); | |
| var shownTime = TimelineWindow.instance.state.timeAreaShownRange; | |
| do | |
| { | |
| AddEdge(edges, d, false); | |
| d += l; | |
| } | |
| while (d < shownTime.y); | |
| } | |
| else | |
| { | |
| AddEdge(edges, d, false); | |
| } | |
| } | |
| } | |
| if (manipulateEdges == ManipulateEdges.Left) | |
| { | |
| var clipInfo = AnimationClipCurveCache.Instance.GetCurveInfo(clip.animationClip); | |
| if (clipInfo != null && clipInfo.keyTimes.Any()) | |
| AddEdge(edges, clip.FromLocalTimeUnbound(clipInfo.keyTimes.Min()), false); | |
| } | |
| } | |
| } | |
| return edges; | |
| } | |
| public bool ShouldSnapTo(ISnappable snappable) | |
| { | |
| return true; | |
| } | |
| bool ShowDrillIcon(PlayableDirector resolver) | |
| { | |
| if (!m_ShowDrillIcon.HasValue || TimelineWindow.instance.hierarchyChangedThisFrame) | |
| { | |
| var nestable = m_ClipEditor.supportsSubTimelines; | |
| m_ShowDrillIcon = nestable && resolver != null; | |
| if (m_ShowDrillIcon.Value) | |
| { | |
| s_TempSubDirectors.Clear(); | |
| try | |
| { | |
| m_ClipEditor.GetSubTimelines(clip, resolver, s_TempSubDirectors); | |
| } | |
| catch (Exception e) | |
| { | |
| Debug.LogException(e); | |
| } | |
| m_ShowDrillIcon &= s_TempSubDirectors.Count > 0; | |
| } | |
| } | |
| return m_ShowDrillIcon.Value; | |
| } | |
| static void AddEdge(List<Edge> edges, double time, bool showEdgeHint = true) | |
| { | |
| var shownTime = TimelineWindow.instance.state.timeAreaShownRange; | |
| if (time >= shownTime.x && time <= shownTime.y) | |
| edges.Add(new Edge(time, showEdgeHint)); | |
| } | |
| public void SelectCurves() | |
| { | |
| SelectionManager.SelectOnly(clip); | |
| SelectionManager.SelectInlineCurveEditor(this); | |
| } | |
| public void ValidateCurvesSelection() | |
| { | |
| if (!IsSelected()) //if clip is not selected, deselect the inline curve | |
| SelectionManager.SelectInlineCurveEditor(null); | |
| } | |
| } | |
| } | |