using System;
using System.Collections.Generic;
using System.Linq;
using UnityEngine;
using UnityEngine.Timeline;
namespace UnityEditor.Timeline
{
partial class TimelineWindow
{
///
/// The public Breadcrumb navigation controller, accessible through TimelineEditorWindow
///
public override TimelineNavigator navigator => new TimelineNavigator(this);
///
/// Implementation of TimelineNavigator
///
///
/// Always use TimelineNavigator, not this class.
///
/// The class acts as a handle on the TimelineWindow, and lets users navigate the breadcrumbs and dive into subtimelines
///
internal class TimelineNavigatorImpl
{
///
///
///
///
public TimelineNavigatorImpl(IWindowStateProvider window)
{
if (window == null)
throw new ArgumentNullException(nameof(window),
"TimelineNavigator cannot be used with a null window");
m_Window = window;
}
///
/// Creates a SequenceContext from the top of the breadcrumb stack
///
///
public SequenceContext GetCurrentContext()
{
return GetBreadcrumbs().LastOrDefault();
}
///
/// Creates a SequenceContext from the second to last breadcrumb in the list
///
/// Valid context if there is a parent, Invalid context otherwise
public SequenceContext GetParentContext()
{
//If the edit sequence is the master sequence, there is no parent context
if (windowState.editSequence == windowState.masterSequence)
return SequenceContext.Invalid;
var contexts = GetBreadcrumbs();
var length = contexts.Count();
return contexts.ElementAtOrDefault(length - 2);
}
///
/// Creates a SequenceContext from the top of the breadcrumb stack
///
/// Always returns a valid SequenceContext
public SequenceContext GetRootContext()
{
return GetBreadcrumbs().FirstOrDefault();
}
///
/// Creates SequenceContexts for all the child Timelines
///
/// Collection of SequenceContexts. Can be empty if there are no valid child contexts
public IEnumerable GetChildContexts()
{
return windowState.GetSubSequences();
}
///
/// Creates SequenceContexts from the breadcrumb stack, from top to bottom
///
/// Collection of SequenceContexts. Should never be empty
public IEnumerable GetBreadcrumbs()
{
return CollectBreadcrumbContexts();
}
///
/// Changes the current Timeline shown in the TimelineWindow to a new SequenceContext (if different and valid)
///
///
/// Should only ever accept SequenceContexts that are in the breadcrumbs. SetTimeline is the proper
/// method to use to switch root Timelines.
///
/// A valid SequenceContext. should always be found in the breadcrumbs
/// The context is not valid
/// The context is not a valid navigation destination.
public void NavigateTo(SequenceContext context)
{
if (!context.IsValid())
throw new ArgumentException(
$"Argument {nameof(context)} is not valid. Check validity with SequenceContext.IsValid.");
//If the provided context is the current context
if (windowState.editSequence.hostClip == context.clip &&
windowState.editSequence.director == context.director &&
windowState.editSequence.asset == context.director.playableAsset)
{
return; // Nothing to do
}
if (context.clip == null)
{
if (context.director != windowState.masterSequence.director)
throw new InvalidOperationException($"{nameof(context)} is not a valid destination in this context. " +
$"To change the root context, use TimelineEditorWindow.SetTimeline instead.");
}
var children = GetChildContexts().ToArray();
var breadcrumbs = CollectBreadcrumbContexts().ToArray();
if (!children.Contains(context) && !breadcrumbs.Contains(context))
{
throw new InvalidOperationException(
"The provided SequenceContext is not a valid destination. " +
"Use GetChildContexts or GetBreadcrumbs to acquire valid destination contexts.");
}
if (children.Contains(context))
{
windowState.SetCurrentSequence(context.director.playableAsset as TimelineAsset, context.director, context.clip);
return;
}
var idx = Array.IndexOf(breadcrumbs, context);
if (idx != -1)
{
windowState.PopSequencesUntilCount(idx + 1);
}
}
private IWindowState windowState
{
get
{
if (m_Window == null || m_Window.windowState == null)
throw new InvalidOperationException("The Window associated to this instance has been destroyed");
return m_Window.windowState;
}
}
private IEnumerable CollectBreadcrumbContexts()
{
return windowState.allSequences?.Select(s => new SequenceContext(s.director, s.hostClip));
}
private readonly IWindowStateProvider m_Window;
}
}
}