// Animancer // https://kybernetik.com.au/animancer // Copyright 2018-2024 Kybernetik //
#if UNITY_EDITOR
using Animancer.TransitionLibraries;
using System;
using System.Collections.Generic;
using System.IO;
using UnityEditor;
using UnityEditor.Callbacks;
using UnityEngine;
using static Animancer.Editor.AnimancerGUI;
using Object = UnityEngine.Object;
namespace Animancer.Editor.TransitionLibraries
{
/// [Editor-Only]
/// An for configuring .
///
/// https://kybernetik.com.au/animancer/api/Animancer.Editor.TransitionLibraries/TransitionLibraryWindow
public class TransitionLibraryWindow :
SerializedDataEditorWindow
{
/************************************************************************************************************************/
/// Opens a window for the `library`.
public static TransitionLibraryWindow Open(TransitionLibraryAsset library)
=> Open(library, true, typeof(SceneView));
/************************************************************************************************************************/
///
/// Double clicking a
/// opens it in the .
///
[OnOpenAsset]
private static bool OnOpenAsset(int instanceID, int line)
{
var library = EditorUtility.InstanceIDToObject(instanceID) as TransitionLibraryAsset;
if (library == null)
return false;
Open(library);
return true;
}
/************************************************************************************************************************/
/// The current window instance.
public static TransitionLibraryWindow Instance { get; private set; }
/// Is a window currently showing the `library`.
public static bool IsShowing(Object library)
=> Instance != null
&& Instance.SourceObject == library;
/************************************************************************************************************************/
///
public override TransitionLibraryDefinition SourceData
{
get => SourceObject.Definition;
set => SourceObject.Definition = value;
}
/************************************************************************************************************************/
[SerializeField]
private TransitionLibrarySelection _Selection;
/// Manages the objects which can be selected within a library.
public TransitionLibrarySelection Selection
=> AnimancerEditorUtilities.FindOrCreate(ref _Selection);
/************************************************************************************************************************/
[SerializeReference]
private List _Pages;
[SerializeField]
private int _CurrentPage;
/// The currently selected page.
public TransitionLibraryWindowPage CurrentPage
{
get
{
_CurrentPage = Mathf.Clamp(_CurrentPage, 0, _Pages.Count - 1);
return _Pages[_CurrentPage];
}
}
/************************************************************************************************************************/
/// Object highlight manager.
public readonly TransitionLibraryWindowHighlighter
Highlighter = new();
/************************************************************************************************************************/
/// Called when an object is selected.
private void OnSelectionChange()
{
if (_Selection != null)
_Selection.OnSelectionChange();
var library = UnityEditor.Selection.activeObject as TransitionLibraryAsset;
if (library != null && library != SourceObject)
SetAndCaptureSource(library);
}
/************************************************************************************************************************/
///
protected override void OnEnable()
{
base.OnEnable();
Instance = this;
wantsMouseMove = true;
// MainStageView, CanvasGroup Icon, GridLayoutGroup Icon.
titleContent = EditorGUIUtility.IconContent("CanvasGroup Icon");
titleContent.text = "Transition Library";
AnimancerEditorUtilities.InstantiateDerivedTypes(ref _Pages);
for (int i = 0; i < _Pages.Count; i++)
_Pages[i].Window = this;
OnSelectionChange();
}
/************************************************************************************************************************/
///
protected override void OnDisable()
{
base.OnDisable();
if (Instance == this)
Instance = null;
}
/************************************************************************************************************************/
///
protected override void OnDestroy()
{
base.OnDestroy();
DestroyImmediate(_Selection);
}
/************************************************************************************************************************/
/// Draws the GUI of this window.
protected virtual void OnGUI()
{
if (SourceObject == null)
{
GUILayout.Label("No Transition Library has been selected");
return;
}
DoHeaderGUI();
DoBodyGUI();
}
/************************************************************************************************************************/
///
protected override void CaptureData()
{
base.CaptureData();
Data.SortAliases();
}
/************************************************************************************************************************/
///
public override void Apply()
{
base.Apply();
for (int i = 0; i < Data.Transitions.Length; i++)
{
var transition = Data.Transitions[i];
if (EditorUtility.IsPersistent(transition))
continue;
AssetDatabase.AddObjectToAsset(transition, SourceObject);
}
}
/************************************************************************************************************************/
private static ButtonGroupStyles _ApplyRevertStyles;
/// Draws the header GUI.
private void DoHeaderGUI()
{
if (_ApplyRevertStyles.left == null)
_ApplyRevertStyles = new(
EditorStyles.toolbarButton,
EditorStyles.toolbarButton,
EditorStyles.toolbarButton);
GUILayout.BeginHorizontal();
var style = EditorStyles.toolbar;
var applyRevertWidth = CalculateApplyRevertWidth(_ApplyRevertStyles) - StandardSpacing - 1;
var area = GUILayoutUtility.GetRect(position.width, style.fixedHeight);
var currentEvent = Event.current;
if (currentEvent.type == EventType.Repaint)
style.Draw(area, false, false, false, false);
var pageArea = StealFromLeft(ref area, PageSelectionWidth);
var applyRevertArea = StealFromRight(ref area, applyRevertWidth);
var pathArea = area;
DoPageSelectionDropdown(pageArea);
DoAssetPathButton(pathArea, currentEvent);
DoApplyRevertGUI(applyRevertArea, _ApplyRevertStyles);
GUILayout.EndHorizontal();
}
/************************************************************************************************************************/
[NonSerialized]
private float _PageSelectionWidth;
private float PageSelectionWidth
{
get
{
if (_PageSelectionWidth == 0)
{
for (int i = 0; i < _Pages.Count; i++)
{
_PageSelectionWidth = Math.Max(
_PageSelectionWidth,
EditorStyles.toolbarDropDown.CalculateWidth(_Pages[i].DisplayName));
}
}
return _PageSelectionWidth;
}
}
/************************************************************************************************************************/
/// Draws a dropdown button for selecting the .
private void DoPageSelectionDropdown(Rect area)
{
using (var label = PooledGUIContent.Acquire(CurrentPage.DisplayName, CurrentPage.HelpTooltip))
if (!EditorGUI.DropdownButton(area, label, FocusType.Passive, EditorStyles.toolbarDropDown))
return;
var menu = new GenericMenu();
for (int i = 0; i < _Pages.Count; i++)
{
var index = i;
var page = _Pages[index];
menu.AddItem(
new(page.DisplayName),
_CurrentPage == index,
() => _CurrentPage = index);
}
menu.AddSeparator("");
menu.AddItem(
new("Documentation"),
false,
() => Application.OpenURL(Strings.DocsURLs.TransitionLibraries));
menu.ShowAsContext();
}
/************************************************************************************************************************/
private static GUIStyle _AssetPathStyle;
private readonly GUIContent AssetPath = new();
/// Draws the asset path of the target library and selects it if clicked.
private void DoAssetPathButton(Rect area, Event currentEvent)
{
_AssetPathStyle ??= new(EditorStyles.toolbarButton)
{
richText = true,
alignment = TextAnchor.MiddleRight,
fontStyle = FontStyle.Italic,
fontSize = (int)(EditorStyles.toolbarButton.fontSize * 0.8f),
};
if (currentEvent.type == EventType.Repaint)
{
var assetPath = AssetDatabase.GetAssetPath(SourceObject);
if (string.IsNullOrEmpty(assetPath))
{
AssetPath.text = "The target Transition Library isn't saved as an asset.";
AssetPath.tooltip = null;
}
else if (AssetPath.tooltip != assetPath)
{
AssetPath.tooltip = assetPath;
var directory = Path.GetDirectoryName(assetPath).Replace('\\', '/');
var file = Path.GetFileNameWithoutExtension(assetPath);
assetPath = $"{directory}/{file}";
AssetPath.text = assetPath;
}
}
if (GUI.Button(area, AssetPath, _AssetPathStyle))
{
if (Selection.Selected != (object)SourceObject)
Selection.Select(this, SourceObject, -1, TransitionLibrarySelection.SelectionType.Library);
else
EditorGUIUtility.PingObject(SourceObject);
}
}
/************************************************************************************************************************/
/// Draws the .
private void DoBodyGUI()
{
GUILayout.FlexibleSpace();
var area = GUILayoutUtility.GetLastRect();
area.width = position.width;
EditorGUI.DrawRect(area, Grey(0.2f, 0.5f));
if (_Pages.Count > 0)
{
Highlighter.BeginGUI(area);
CurrentPage?.OnGUI(area);
Highlighter.EndGUI(this);
}
}
/************************************************************************************************************************/
}
}
#endif