using System;
using System.Collections.Generic;
using System.Linq;
using UnityEditor.Timeline.Actions;
using UnityEngine;
using UnityEngine.Playables;
using UnityEngine.Timeline;
namespace UnityEditor.Timeline
{
///
/// Information currently being edited in the Timeline Editor Window.
///
public static class TimelineEditor
{
///
/// The PlayableDirector associated with the timeline currently being shown in the Timeline window.
///
public static PlayableDirector inspectedDirector => state?.editSequence.director;
///
/// The PlayableDirector responsible for the playback of the timeline currently being shown in the Timeline window.
///
public static PlayableDirector masterDirector => state?.masterSequence.director;
///
/// The TimelineAsset currently being shown in the Timeline window.
///
public static TimelineAsset inspectedAsset => state?.editSequence.asset;
///
/// The TimelineAsset at the root of the hierarchy currently being shown in the Timeline window.
///
public static TimelineAsset masterAsset => state?.masterSequence.asset;
///
/// The PlayableDirector currently being shown in the Timeline Editor Window.
///
[Obsolete("playableDirector is ambiguous. Please select either inspectedDirector or masterDirector instead.", false)]
public static PlayableDirector playableDirector
{
get { return inspectedDirector; }
}
///
/// The TimelineAsset currently being shown in the Timeline Editor Window.
///
[Obsolete("timelineAsset is ambiguous. Please select either inspectedAsset or masterAsset instead.", false)]
public static TimelineAsset timelineAsset
{
get { return inspectedAsset; }
}
///
///
/// Refreshes the different components affected by the currently inspected
/// , based on the provided.
///
///
/// For better performance, it is recommended that you invoke this method once, after you modify the
/// . You should also combine reasons using the | operator.
///
///
///
/// Note: This operation is not synchronous. It is performed during the next GUI loop.
///
/// The reason why a refresh should be performed.
public static void Refresh(RefreshReason reason)
{
if (state == null)
return;
if ((reason & RefreshReason.ContentsAddedOrRemoved) != 0)
{
state.Refresh();
}
else if ((reason & RefreshReason.ContentsModified) != 0)
{
state.rebuildGraph = true;
}
else if ((reason & RefreshReason.SceneNeedsUpdate) != 0)
{
state.Evaluate();
}
window.Repaint();
}
internal static TimelineWindow window => TimelineWindow.instance;
internal static WindowState state => window == null ? null : window.state;
internal static readonly Clipboard clipboard = new Clipboard();
///
/// The list of clips selected in the TimelineEditor.
///
public static TimelineClip[] selectedClips
{
get { return Selection.GetFiltered(SelectionMode.Unfiltered).Select(e => e.clip).Where(x => x != null).ToArray(); }
set
{
if (value == null || value.Length == 0)
{
Selection.objects = null;
}
else
{
var objects = new List();
foreach (var clip in value)
{
if (clip == null)
continue;
var editorClip = EditorClipFactory.GetEditorClip(clip);
if (editorClip != null)
objects.Add(editorClip);
}
Selection.objects = objects.ToArray();
}
}
}
///
/// The clip selected in the TimelineEditor.
///
///
/// If there are multiple clips selected, this property returns the first clip.
///
public static TimelineClip selectedClip
{
get
{
var editorClip = Selection.activeObject as EditorClip;
if (editorClip != null)
return editorClip.clip;
return null;
}
set
{
var editorClip = (value != null) ? EditorClipFactory.GetEditorClip(value) : null;
Selection.activeObject = editorClip;
}
}
///
/// Local time (in seconds) of the inspected sequence.
///
/// Thrown if timeline window is not available.
internal static double inspectedSequenceTime
{
get => state?.editSequence.time ?? 0;
set
{
if (state == null)
throw new InvalidOperationException("Cannot set time. Timeline Window may not be available.");
state.editSequence.time = value;
}
}
///
/// Global time (in seconds) of the master timeline.
/// Same as local time if not inspected a subtimeline.
///
/// Thrown if timeline window is not available.
internal static double masterSequenceTime
{
get => state?.editSequence.ToGlobalTime(state.editSequence.time) ?? 0;
set
{
if (state == null)
throw new InvalidOperationException("Cannot set time. Timeline Window may not be available.");
state.masterSequence.time = value;
}
}
///
/// Visible time range (in seconds) in Editor.
/// x : min time
/// y : max time
///
/// Thrown if timeline window is not available.
internal static Vector2 visibleTimeRange
{
get => state?.timeAreaShownRange ?? TimelineAssetViewModel.TimeAreaDefaultRange;
set
{
if (state == null)
throw new InvalidOperationException("Cannot set visible time range. Timeline Window may not be available.");
state.timeAreaShownRange = value;
}
}
internal static ActionContext CurrentContext(Vector2? mousePos = null)
{
return new ActionContext
{
invocationTime = mousePos != null ? TimelineHelpers.GetCandidateTime(mousePos) : (double?)null,
clips = SelectionManager.SelectedClips(),
tracks = SelectionManager.SelectedTracks(),
markers = SelectionManager.SelectedMarkers(),
timeline = inspectedAsset,
director = inspectedDirector
};
}
}
///
/// uses these flags to determine what needs to be refreshed or updated.
///
///
/// Use the | operator to combine flags.
///
///
///
///
[Flags]
public enum RefreshReason
{
///
/// Use this flag when a change to the Timeline requires that the Timeline window be redrawn.
///
WindowNeedsRedraw = 1 << 0,
///
/// Use this flag when a change to the Timeline requires that the Scene be updated.
///
SceneNeedsUpdate = 1 << 1,
///
/// Use this flag when a Timeline element was modified.
///
ContentsModified = 1 << 2,
///
/// Use this flag when an element was added to or removed from the Timeline.
///
ContentsAddedOrRemoved = 1 << 3
}
}