358 lines
13 KiB
C#
358 lines
13 KiB
C#
// 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
|
|
{
|
|
/// <summary>[Editor-Only]
|
|
/// An <see cref="EditorWindow"/> for configuring <see cref="TransitionLibraryAsset"/>.
|
|
/// </summary>
|
|
/// https://kybernetik.com.au/animancer/api/Animancer.Editor.TransitionLibraries/TransitionLibraryWindow
|
|
public class TransitionLibraryWindow :
|
|
SerializedDataEditorWindow<TransitionLibraryAsset, TransitionLibraryDefinition>
|
|
{
|
|
/************************************************************************************************************************/
|
|
|
|
/// <summary>Opens a window for the `library`.</summary>
|
|
public static TransitionLibraryWindow Open(TransitionLibraryAsset library)
|
|
=> Open<TransitionLibraryWindow>(library, true, typeof(SceneView));
|
|
|
|
/************************************************************************************************************************/
|
|
|
|
/// <summary>
|
|
/// Double clicking a <see cref="TransitionLibraryAsset"/>
|
|
/// opens it in the <see cref="TransitionLibraryWindow"/>.
|
|
/// </summary>
|
|
[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;
|
|
}
|
|
|
|
/************************************************************************************************************************/
|
|
|
|
/// <summary>The current window instance.</summary>
|
|
public static TransitionLibraryWindow Instance { get; private set; }
|
|
|
|
/// <summary>Is a window currently showing the `library`.</summary>
|
|
public static bool IsShowing(Object library)
|
|
=> Instance != null
|
|
&& Instance.SourceObject == library;
|
|
|
|
/************************************************************************************************************************/
|
|
|
|
/// <inheritdoc/>
|
|
public override TransitionLibraryDefinition SourceData
|
|
{
|
|
get => SourceObject.Definition;
|
|
set => SourceObject.Definition = value;
|
|
}
|
|
|
|
/************************************************************************************************************************/
|
|
|
|
[SerializeField]
|
|
private TransitionLibrarySelection _Selection;
|
|
|
|
/// <summary>Manages the objects which can be selected within a library.</summary>
|
|
public TransitionLibrarySelection Selection
|
|
=> AnimancerEditorUtilities.FindOrCreate(ref _Selection);
|
|
|
|
/************************************************************************************************************************/
|
|
|
|
[SerializeReference]
|
|
private List<TransitionLibraryWindowPage> _Pages;
|
|
|
|
[SerializeField]
|
|
private int _CurrentPage;
|
|
|
|
/// <summary>The currently selected page.</summary>
|
|
public TransitionLibraryWindowPage CurrentPage
|
|
{
|
|
get
|
|
{
|
|
_CurrentPage = Mathf.Clamp(_CurrentPage, 0, _Pages.Count - 1);
|
|
return _Pages[_CurrentPage];
|
|
}
|
|
}
|
|
|
|
/************************************************************************************************************************/
|
|
|
|
/// <summary>Object highlight manager.</summary>
|
|
public readonly TransitionLibraryWindowHighlighter
|
|
Highlighter = new();
|
|
|
|
/************************************************************************************************************************/
|
|
|
|
/// <summary>Called when an object is selected.</summary>
|
|
private void OnSelectionChange()
|
|
{
|
|
if (_Selection != null)
|
|
_Selection.OnSelectionChange();
|
|
|
|
var library = UnityEditor.Selection.activeObject as TransitionLibraryAsset;
|
|
if (library != null && library != SourceObject)
|
|
SetAndCaptureSource(library);
|
|
}
|
|
|
|
/************************************************************************************************************************/
|
|
|
|
/// <inheritdoc/>
|
|
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();
|
|
}
|
|
|
|
/************************************************************************************************************************/
|
|
|
|
/// <inheritdoc/>
|
|
protected override void OnDisable()
|
|
{
|
|
base.OnDisable();
|
|
|
|
if (Instance == this)
|
|
Instance = null;
|
|
}
|
|
|
|
/************************************************************************************************************************/
|
|
|
|
/// <inheritdoc/>
|
|
protected override void OnDestroy()
|
|
{
|
|
base.OnDestroy();
|
|
DestroyImmediate(_Selection);
|
|
}
|
|
|
|
/************************************************************************************************************************/
|
|
|
|
/// <summary>Draws the GUI of this window.</summary>
|
|
protected virtual void OnGUI()
|
|
{
|
|
if (SourceObject == null)
|
|
{
|
|
GUILayout.Label("No Transition Library has been selected");
|
|
return;
|
|
}
|
|
|
|
DoHeaderGUI();
|
|
DoBodyGUI();
|
|
}
|
|
|
|
/************************************************************************************************************************/
|
|
|
|
/// <inheritdoc/>
|
|
protected override void CaptureData()
|
|
{
|
|
base.CaptureData();
|
|
Data.SortAliases();
|
|
}
|
|
|
|
/************************************************************************************************************************/
|
|
|
|
/// <inheritdoc/>
|
|
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;
|
|
|
|
/// <summary>Draws the header GUI.</summary>
|
|
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;
|
|
}
|
|
}
|
|
|
|
/************************************************************************************************************************/
|
|
|
|
/// <summary>Draws a dropdown button for selecting the <see cref="CurrentPage"/>.</summary>
|
|
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();
|
|
|
|
/// <summary>Draws the asset path of the target library and selects it if clicked.</summary>
|
|
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}/<b>{file}</b>";
|
|
|
|
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);
|
|
}
|
|
}
|
|
|
|
/************************************************************************************************************************/
|
|
|
|
/// <summary>Draws the <see cref="CurrentPage"/>.</summary>
|
|
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
|
|
|