using System; using System.Collections.Generic; using System.Linq; using System.Text; using UnityEngine; using UnityEngine.Timeline; namespace UnityEditor.Timeline { class TrimClip : Manipulator { class TrimClipAttractionHandler : IAttractionHandler { public void OnAttractedEdge(IAttractable attractable, ManipulateEdges manipulateEdges, AttractedEdge edge, double time) { var clipGUI = attractable as TimelineClipGUI; if (clipGUI == null) return; var clipItem = ItemsUtils.ToItem(clipGUI.clip); if (manipulateEdges == ManipulateEdges.Right) { bool affectTimeScale = IsAffectingTimeScale(clipGUI.clip); EditMode.TrimEnd(clipItem, time, affectTimeScale); } else if (manipulateEdges == ManipulateEdges.Left) { bool affectTimeScale = IsAffectingTimeScale(clipGUI.clip); EditMode.TrimStart(clipItem, time, affectTimeScale); } } private bool IsAffectingTimeScale(TimelineClip clip) { bool autoScale = (clip.clipCaps & ClipCaps.AutoScale) == ClipCaps.AutoScale; // TODO Do not use Event.current from here. bool affectTimeScale = (autoScale && (Event.current.modifiers != EventModifiers.Shift)) || (!autoScale && (Event.current.modifiers == EventModifiers.Shift)); return affectTimeScale; } } bool m_IsCaptured; TimelineClipHandle m_TrimClipHandler; double m_OriginalDuration; double m_OriginalTimeScale; double m_OriginalEaseInDuration; double m_OriginalEaseOutDuration; bool m_UndoSaved; SnapEngine m_SnapEngine; readonly StringBuilder m_OverlayText = new StringBuilder(); readonly List m_OverlayStrings = new List(); static readonly double kEpsilon = 0.0000001; protected override bool MouseDown(Event evt, WindowState state) { var handle = PickerUtils.TopmostPickedItem() as TimelineClipHandle; if (handle == null) return false; if (handle.clipGUI.clip.parentTrack != null && handle.clipGUI.clip.parentTrack.lockedInHierarchy) return false; m_TrimClipHandler = handle; m_IsCaptured = true; state.AddCaptured(this); m_UndoSaved = false; var clip = m_TrimClipHandler.clipGUI.clip; m_OriginalDuration = clip.duration; m_OriginalTimeScale = clip.timeScale; m_OriginalEaseInDuration = clip.easeInDuration; m_OriginalEaseOutDuration = clip.easeOutDuration; RefreshOverlayStrings(m_TrimClipHandler, state); // in ripple trim, the right edge moves and needs to snap var edges = ManipulateEdges.Right; if (EditMode.editType != EditMode.EditType.Ripple && m_TrimClipHandler.trimDirection == TrimEdge.Start) edges = ManipulateEdges.Left; m_SnapEngine = new SnapEngine(m_TrimClipHandler.clipGUI, new TrimClipAttractionHandler(), edges, state, evt.mousePosition); EditMode.BeginTrim(ItemsUtils.ToItem(clip), m_TrimClipHandler.trimDirection); return true; } protected override bool MouseUp(Event evt, WindowState state) { if (!m_IsCaptured) return false; m_IsCaptured = false; m_TrimClipHandler = null; m_UndoSaved = false; m_SnapEngine = null; EditMode.FinishTrim(); state.captured.Clear(); return true; } protected override bool MouseDrag(Event evt, WindowState state) { if (state.editSequence.isReadOnly) return false; if (!m_IsCaptured) return false; var uiClip = m_TrimClipHandler.clipGUI; if (!m_UndoSaved) { UndoExtensions.RegisterClip(uiClip.clip, "Trim Clip"); if (TimelineUtility.IsRecordableAnimationClip(uiClip.clip)) { TimelineUndo.PushUndo(uiClip.clip.animationClip, "Trim Clip"); } m_UndoSaved = true; } //Reset to original ease values. The trim operation will calculate the proper blend values. uiClip.clip.easeInDuration = m_OriginalEaseInDuration; uiClip.clip.easeOutDuration = m_OriginalEaseOutDuration; if (m_SnapEngine != null) m_SnapEngine.Snap(evt.mousePosition, evt.modifiers); RefreshOverlayStrings(m_TrimClipHandler, state); if (Selection.activeObject != null) EditorUtility.SetDirty(Selection.activeObject); // updates the duration of the graph without rebuilding state.UpdateRootPlayableDuration(state.editSequence.duration); return true; } public override void Overlay(Event evt, WindowState state) { if (!m_IsCaptured) return; EditMode.DrawTrimGUI(state, m_TrimClipHandler.clipGUI, m_TrimClipHandler.trimDirection); bool trimStart = m_TrimClipHandler.trimDirection == TrimEdge.Start; TimeIndicator.Draw(state, trimStart ? m_TrimClipHandler.clipGUI.start : m_TrimClipHandler.clipGUI.end); if (m_SnapEngine != null) m_SnapEngine.OnGUI(trimStart, !trimStart); if (m_OverlayStrings.Count > 0) { const float padding = 4.0f; var labelStyle = TimelineWindow.styles.tinyFont; var longestLine = labelStyle.CalcSize( new GUIContent(m_OverlayStrings.Aggregate("", (max, cur) => max.Length > cur.Length ? max : cur))); var stringLength = longestLine.x + padding; var lineHeight = longestLine.y + padding; var r = new Rect(evt.mousePosition.x - (stringLength / 2.0f), m_TrimClipHandler.clipGUI.rect.yMax, stringLength, lineHeight); foreach (var s in m_OverlayStrings) { GUI.Label(r, s, labelStyle); r.y += lineHeight; } } } void RefreshOverlayStrings(TimelineClipHandle handle, WindowState state) { m_OverlayStrings.Clear(); m_OverlayText.Length = 0; var differenceDuration = handle.clipGUI.clip.duration - m_OriginalDuration; bool hasDurationDelta = Math.Abs(differenceDuration) > kEpsilon; if (state.timeInFrames) { var durationInFrame = handle.clipGUI.clip.duration * state.referenceSequence.frameRate; m_OverlayText.Append("duration: ").Append(durationInFrame.ToString("f2")).Append(" frames"); if (hasDurationDelta) { m_OverlayText.Append(" ("); if (differenceDuration > 0.0) m_OverlayText.Append("+"); var valueInFrame = differenceDuration * state.referenceSequence.frameRate; m_OverlayText.Append(valueInFrame.ToString("f2")).Append(" frames)"); } } else { m_OverlayText.Append("duration: ").Append(handle.clipGUI.clip.duration.ToString("f2")).Append("s"); if (hasDurationDelta) { m_OverlayText.Append(" ("); if (differenceDuration > 0.0) m_OverlayText.Append("+"); m_OverlayText.Append(differenceDuration.ToString("f2")).Append("s)"); } } m_OverlayStrings.Add(m_OverlayText.ToString()); m_OverlayText.Length = 0; var differenceSpeed = m_OriginalTimeScale - handle.clipGUI.clip.timeScale; if (Math.Abs(differenceSpeed) > kEpsilon) { m_OverlayText.Append("speed: ").Append(handle.clipGUI.clip.timeScale.ToString("p2")); m_OverlayText.Append(" ("); if (differenceSpeed > 0.0) m_OverlayText.Append("+"); m_OverlayText.Append(differenceSpeed.ToString("p2")).Append(")"); m_OverlayStrings.Add(m_OverlayText.ToString()); } } } }