Skip to content

Instantly share code, notes, and snippets.

@gengen1988
Last active August 20, 2022 17:23
Show Gist options
  • Save gengen1988/be7274d1f38bfbe188848b89186b1753 to your computer and use it in GitHub Desktop.
Save gengen1988/be7274d1f38bfbe188848b89186b1753 to your computer and use it in GitHub Desktop.
An Unity editor script to help navigating in hierarchy or project browser
using System.Collections.Generic;
using System.Linq;
using UnityEditor;
using UnityEngine;
[InitializeOnLoad]
public class SelectionHistory
{
private const int MAX_HISTORY_COUNT = 10;
public static SelectionHistory Instance { get; private set; }
private bool skipRecord;
private SelectionSnapshot currentState;
private HistoryStack<SelectionSnapshot> back, forward;
static SelectionHistory()
{
Instance = new SelectionHistory();
Instance.Start();
}
private void Start()
{
currentState = TakeSnapshot();
back = new HistoryStack<SelectionSnapshot>(MAX_HISTORY_COUNT);
forward = new HistoryStack<SelectionSnapshot>(MAX_HISTORY_COUNT);
Selection.selectionChanged += OnSelectionChanged;
}
private void OnSelectionChanged()
{
// skip back or forward changes
if (skipRecord)
{
skipRecord = false;
}
else
{
RecordSelection();
}
currentState = TakeSnapshot();
}
[MenuItem("Selection / Back _[")]
private static void MenuItemBack()
{
Instance.Back();
}
[MenuItem("Selection / Forward _]")]
private static void MenuItemForward()
{
Instance.Forward();
}
private void RecordSelection()
{
// do not record empty entry
if (IsDeselected())
{
return;
}
// do not record same entry
SelectionSnapshot latestHistory = back.Peek();
if (latestHistory != null
&& latestHistory.ActiveObject == currentState.ActiveObject
&& latestHistory.Objects.SequenceEqual(currentState.Objects))
{
return;
}
back.Push(currentState);
forward.Clear();
}
private void Back()
{
if (back.Count == 0)
{
return;
}
// prevent forward to null
if (!IsDeselected())
{
forward.Push(currentState);
}
SelectionSnapshot snapshot = back.Pop();
RestoreSnapshot(snapshot);
}
private void Forward()
{
if (forward.Count == 0)
{
return;
}
back.Push(currentState);
SelectionSnapshot snapshot = forward.Pop();
RestoreSnapshot(snapshot);
}
private SelectionSnapshot TakeSnapshot()
{
return new SelectionSnapshot
{
Window = EditorWindow.focusedWindow,
ActiveObject = Selection.activeObject,
Objects = Selection.objects
};
}
private void RestoreSnapshot(SelectionSnapshot snapshot)
{
skipRecord = true;
Selection.activeObject = snapshot.ActiveObject;
Selection.objects = snapshot.Objects;
snapshot.Window.Focus(); // TODO handle locked ProjectBrowser
EditorGUIUtility.PingObject(snapshot.ActiveObject);
}
private bool IsDeselected()
{
return currentState == null || currentState.ActiveObject == null;
}
}
public class HistoryStack<T>
{
private readonly int size;
private readonly LinkedList<T> collection;
public HistoryStack(int size)
{
this.size = size;
collection = new LinkedList<T>();
}
public void Push(T snapshot)
{
collection.AddLast(snapshot);
// remove too old records
while (collection.Count > size)
{
collection.RemoveFirst();
}
}
public T Pop()
{
T snapshot = collection.Last.Value;
collection.RemoveLast();
return snapshot;
}
public T Peek()
{
return collection.Count == 0 ? default : collection.Last.Value;
}
public void Clear()
{
collection.Clear();
}
public int Count => collection.Count;
}
public class SelectionSnapshot
{
public EditorWindow Window;
public Object ActiveObject;
public Object[] Objects;
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment