// Animancer // https://kybernetik.com.au/animancer // Copyright 2018-2026 Kybernetik //
#if UNITY_EDITOR
using Animancer.TransitionLibraries;
using System;
using UnityEditor;
using UnityEngine;
using Object = UnityEngine.Object;
namespace Animancer.Editor.TransitionLibraries
{
/// [Editor-Only]
/// A custom Inspector for .
///
/// https://kybernetik.com.au/animancer/api/Animancer.Editor.TransitionLibraries/TransitionLibrarySelectionEditor
[CustomEditor(typeof(TransitionLibrarySelection), true)]
public class TransitionLibrarySelectionEditor : UnityEditor.Editor
{
/************************************************************************************************************************/
/// Casts the .
public TransitionLibrarySelection Target
=> target as TransitionLibrarySelection;
/************************************************************************************************************************/
///
public override void OnInspectorGUI()
{
var target = Target;
if (target == null || !target.Validate())
return;
EditorGUI.BeginChangeCheck();
switch (target.Type)
{
case TransitionLibrarySelection.SelectionType.Library:
DoNestedEditorGUI(target.Selected as TransitionLibraryAsset, "Transition Library");
break;
case TransitionLibrarySelection.SelectionType.FromTransition:
case TransitionLibrarySelection.SelectionType.ToTransition:
DoTransitionGUI(target.Selected as TransitionAssetBase);
break;
case TransitionLibrarySelection.SelectionType.Modifier:
DoModifierGUI(target, (TransitionModifierDefinition)target.Selected);
break;
case TransitionLibrarySelection.SelectionType.Group:
DoGroupGUI(target, (TransitionGroup)target.Selected);
break;
default:
target.Deselect();
break;
}
if (EditorGUI.EndChangeCheck())
target.Window.Repaint();
}
/************************************************************************************************************************/
#region Nested Editor
/************************************************************************************************************************/
[NonSerialized] private readonly CachedEditor NestedEditor = new();
[NonSerialized] private readonly CachedEditor NestedEditor2 = new();
/************************************************************************************************************************/
/// Draws the for the `target`.
private void DoNestedEditorGUI(T target, string referenceLabel)
where T : Object
{
using (new EditorGUI.DisabledScope(true))
AnimancerGUI.DoObjectFieldGUI(referenceLabel, target, false);
var editor = NestedEditor.GetEditor(target);
if (editor != null)
editor.OnInspectorGUI();
}
/************************************************************************************************************************/
/// Cleans up any nested editors.
protected virtual void OnDestroy()
{
NestedEditor.Dispose();
NestedEditor2.Dispose();
}
/************************************************************************************************************************/
#endregion
/************************************************************************************************************************/
#region Transitions
/************************************************************************************************************************/
/// Draws the GUI for the `transition`.
private void DoTransitionGUI(
TransitionAssetBase transition)
{
DoTransitionNameGUI(transition);
DoNestedEditorGUI(transition, "Transition Asset");
}
/************************************************************************************************************************/
/// Draws a field for editing the name of the `transition`.
private void DoTransitionNameGUI(
TransitionAssetBase transition)
{
var isSubAsset = AssetDatabase.IsSubAsset(transition);
var isMainAsset = !isSubAsset && AssetDatabase.IsMainAsset(transition);
var label = isSubAsset
? "Sub-Asset Name"
: isMainAsset
? "File Name"
: "Name";
EditorGUI.BeginChangeCheck();
var name = TransitionModifierTableGUI.GetTransitionName(transition);
name = EditorGUILayout.DelayedTextField(label, name);
if (EditorGUI.EndChangeCheck() && transition != null)
{
transition.SetName(name);
if (isSubAsset)
{
AssetDatabase.SaveAssets();
}
else if (isMainAsset)
{
AssetDatabase.RenameAsset(
AssetDatabase.GetAssetPath(transition),
name);
}
}
}
/************************************************************************************************************************/
#endregion
/************************************************************************************************************************/
#region Modifiers
/************************************************************************************************************************/
private static readonly BoolPref
IsFromExpanded = new($"{nameof(TransitionLibrarySelectionEditor)}.{nameof(IsFromExpanded)}"),
IsToExpanded = new($"{nameof(TransitionLibrarySelectionEditor)}.{nameof(IsToExpanded)}");
/************************************************************************************************************************/
/// Draws the GUI for the `modifier`.
private void DoModifierGUI(
TransitionLibrarySelection selection,
TransitionModifierDefinition modifier)
{
var library = selection.Window.Data;
DoTransitionField(library, NestedEditor, IsFromExpanded, modifier.FromIndex, "From");
DoTransitionField(library, NestedEditor2, IsToExpanded, modifier.ToIndex, "To");
if (selection.Window.TryGetPage(out var fadeDurations))
{
var area = AnimancerGUI.LayoutSingleLineRect();
TransitionModifierTableGUI.DoModifierValueGUI(
area,
selection.Window,
fadeDurations,
modifier.FromIndex,
modifier.ToIndex,
"Fade Duration",
false);
}
if (selection.Window.TryGetPage(out var startTimes))
{
var area = AnimancerGUI.LayoutSingleLineRect();
TransitionModifierTableGUI.DoModifierValueGUI(
area,
selection.Window,
startTimes,
modifier.FromIndex,
modifier.ToIndex,
"Start Time",
false);
}
}
/************************************************************************************************************************/
/// Draws the GUI for a transition.
private TransitionAssetBase DoTransitionField(
TransitionLibraryDefinition library,
CachedEditor cachedEditor,
BoolPref isExpanded,
int transitionIndex,
string label)
{
library.TryGetTransition(transitionIndex, out var transition);
var area = AnimancerGUI.LayoutSingleLineRect(AnimancerGUI.SpacingMode.After);
var labelArea = area;
labelArea.width = EditorGUIUtility.labelWidth;
isExpanded.Value = EditorGUI.Foldout(labelArea, isExpanded, GUIContent.none, true);
var enabled = GUI.enabled;
GUI.enabled = false;
AnimancerGUI.DoObjectFieldGUI(area, label, transition, false);
GUI.enabled = enabled;
if (isExpanded)
{
GUILayout.BeginVertical(GUI.skin.box);
var editor = cachedEditor.GetEditor(transition);
editor.OnInspectorGUI();
GUILayout.EndVertical();
}
return transition;
}
/************************************************************************************************************************/
#endregion
/************************************************************************************************************************/
#region Groups
/************************************************************************************************************************/
/// Draws the GUI for the `group`.
private void DoGroupGUI(
TransitionLibrarySelection selection,
TransitionGroup group)
{
group.Name = EditorGUILayout.TextField("Group Name", group.Name);
var enabled = GUI.enabled;
GUI.enabled = false;
EditorGUILayout.LabelField("Transition Count", group.TransitionIndices.Count.ToStringCached());
var transitions = selection.Window.Data.Transitions;
for (int i = 0; i < group.TransitionIndices.Count; i++)
{
var index = group.TransitionIndices[i];
if (!transitions.TryGetObject(index, out var transition))
continue;
EditorGUILayout.ObjectField(
$"Transition {i.ToStringCached()}",
transition,
typeof(TransitionAssetBase),
false);
}
GUI.enabled = enabled;
}
/************************************************************************************************************************/
#endregion
/************************************************************************************************************************/
}
}
#endif