How to find/reveal/focus track in Timeline window?

I'm looking for a way to find/reveal/focus/highlight a track with a specific generic binding in the Timeline window. The idea is that you can drag&drop a GameObject from the Hierarchy to a "Find in Timeline" window, which will then show the track in the Timeline window.

Drag&Drop "Crawler_Timeline" from Hierarchy to "Find in Timeline" window:

8681625--1170414--upload_2022-12-22_20-45-4.png

The Timeline window should then reveal the first track that has the object as its generic binding. In this case, expand all groups and scroll the view to the track:

8681625--1170417--upload_2022-12-22_20-46-28.png

I'm not looking for a textbook solution, I really just need this functionality. Hacks and reflection are perfectly fine for me. The reason for this requirement is that the missing search functionality in Timeline is currently killing our development times, because our timeline asset is rather complex and it's hard to find things.

Here is the code I came up with so far. It's able to find the track, but it's missing how to show the track in the Timeline window.

using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEditor;
using UnityEditor.Timeline;
using UnityEngine.Timeline;

    class FindInTimeline : EditorWindow
    {
        Object m_Obj;

        [MenuItem("Window/Sequencing/Find in Timeline", priority = 300)]
        static void DoMenuItem()
        {
            var wnd = EditorWindow.GetWindow<FindInTimeline>();
            wnd.titleContent = new GUIContent($"Find in Timeline");
            wnd.Show();
        }

        void OnGUI()
        {
            var newObj = EditorGUILayout.ObjectField("Find", m_Obj, typeof(Object), true);
            if (newObj != m_Obj)
            {
                m_Obj = newObj;
                Find(m_Obj);
            }
        }

        void Find(Object obj)
        {
            var director = TimelineEditor.inspectedDirector;
            var timeline = TimelineEditor.inspectedAsset;

            foreach (var track in timeline.GetOutputTracks())
            {
                if (director.GetGenericBinding(track) != obj)
                    continue;

                // now i want to reveal the current track in the Timeline window
                // and focus/highlight it
                Debug.Log(track.name);
            }
        }
    }

You can use this method to reveal a track. Each group track that is part of a track's hierarchy will be opened. This can be done using the public api:

static void Reveal(TrackAsset track)
{
    while (track != null)
    {
        track.SetCollapsed(false);
        track = track.parent as TrackAsset;
    }
    TimelineEditor.Refresh(RefreshReason.ContentsAddedOrRemoved);
}

To scroll to a track, there is no public API to do this. There is an internal method that can do this though:

static void ScrollTo(TrackAsset track)
{
    Selection.activeObject = track;
    InvokeInternalFrameTrack();
}

static void InvokeInternalFrameTrack()
{
    const string keyboardNavigationTypeName = "UnityEditor.Timeline.KeyboardNavigation";
    const string frameTrackHeaderMethodName = "FrameTrackHeader";

    Assembly timelineEditorAssembly = typeof(TimelineEditorWindow).Assembly;
    Type keyboardNavigation = timelineEditorAssembly.GetType(keyboardNavigationTypeName);
    MethodInfo method = keyboardNavigation.GetMethod(frameTrackHeaderMethodName);
    method.Invoke(null, new[] { Type.Missing });
}

Please note that there is no guarantee of support or backwards compatibility in future versions for internal/private methods.

1 Like