File size: 6,805 Bytes
18a519f | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 | using System;
using System.Collections.Generic;
using System.Linq;
using UnityEngine;
using UnityEngine.Timeline;
namespace UnityEditor.Timeline
{
partial class TimelineWindow
{
/// <summary>
/// The public Breadcrumb navigation controller, accessible through TimelineEditorWindow
/// </summary>
public override TimelineNavigator navigator => new TimelineNavigator(this);
/// <summary>
/// Implementation of TimelineNavigator
/// </summary>
/// <remarks>
/// 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
/// </remarks>
internal class TimelineNavigatorImpl
{
/// <summary>
///
/// </summary>
/// <param name="window"></param>
public TimelineNavigatorImpl(IWindowStateProvider window)
{
if (window == null)
throw new ArgumentNullException(nameof(window),
"TimelineNavigator cannot be used with a null window");
m_Window = window;
}
/// <summary>
/// Creates a SequenceContext from the top of the breadcrumb stack
/// </summary>
/// <returns></returns>
public SequenceContext GetCurrentContext()
{
return GetBreadcrumbs().LastOrDefault();
}
/// <summary>
/// Creates a SequenceContext from the second to last breadcrumb in the list
/// </summary>
/// <returns>Valid context if there is a parent, Invalid context otherwise</returns>
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);
}
/// <summary>
/// Creates a SequenceContext from the top of the breadcrumb stack
/// </summary>
/// <returns>Always returns a valid SequenceContext</returns>
public SequenceContext GetRootContext()
{
return GetBreadcrumbs().FirstOrDefault();
}
/// <summary>
/// Creates SequenceContexts for all the child Timelines
/// </summary>
/// <returns>Collection of SequenceContexts. Can be empty if there are no valid child contexts</returns>
public IEnumerable<SequenceContext> GetChildContexts()
{
return windowState.GetSubSequences();
}
/// <summary>
/// Creates SequenceContexts from the breadcrumb stack, from top to bottom
/// </summary>
/// <returns>Collection of SequenceContexts. Should never be empty</returns>
public IEnumerable<SequenceContext> GetBreadcrumbs()
{
return CollectBreadcrumbContexts();
}
/// <summary>
/// Changes the current Timeline shown in the TimelineWindow to a new SequenceContext (if different and valid)
/// </summary>
/// <remarks>
/// Should only ever accept SequenceContexts that are in the breadcrumbs. SetTimeline is the proper
/// method to use to switch root Timelines.
/// </remarks>
/// <param name="context">A valid SequenceContext. <paramref name="context"/> should always be found in the breadcrumbs</param>
/// <exception cref="System.ArgumentException"> The context is not valid</exception>
/// <exception cref="System.InvalidOperationException"> The context is not a valid navigation destination.</exception>
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<SequenceContext> CollectBreadcrumbContexts()
{
return windowState.allSequences?.Select(s => new SequenceContext(s.director, s.hostClip));
}
private readonly IWindowStateProvider m_Window;
}
}
}
|