// Animancer // https://kybernetik.com.au/animancer // Copyright 2018-2024 Kybernetik //
#if UNITY_EDITOR
using System;
using System.Collections.Generic;
using System.IO;
using UnityEditor;
using UnityEditor.AnimatedValues;
using UnityEngine;
using Object = UnityEngine.Object;
namespace Animancer.Editor.Tools
{
partial class AnimancerToolsWindow
{
/// [Editor-Only] [Pro-Only] Base class for tools in the .
///
/// Documentation:
///
/// Animancer Tools
///
/// https://kybernetik.com.au/animancer/api/Animancer.Editor.Tools/Tool
///
[Serializable]
public abstract class Tool : IComparable
{
/************************************************************************************************************************/
private AnimBool _FullAnimator;
private AnimBool _BodyAnimator;
private int _Index;
/************************************************************************************************************************/
/// Is this tool currently visible?
public bool IsVisible => Instance._CurrentTool == _Index || Instance._CurrentTool < 0;
/************************************************************************************************************************/
/// Is the body of this tool currently visible?
public bool IsExpanded
{
get { return Instance._CurrentTool == _Index; }
set
{
if (value)
Instance._CurrentTool = _Index;
else if (IsExpanded)
Instance._CurrentTool = -1;
}
}
/************************************************************************************************************************/
/// Lower numbers display first.
public abstract int DisplayOrder { get; }
/// Compares the to put lower numbers first.
public int CompareTo(Tool other)
=> DisplayOrder.CompareTo(other.DisplayOrder);
/************************************************************************************************************************/
/// The display name of this tool.
public abstract string Name { get; }
/// The usage instructions to display at the top of this tool.
public abstract string Instructions { get; }
/// The URL for the help button in the header to open.
public virtual string HelpURL => Strings.DocsURLs.AnimancerTools;
/// Called whenever the changes.
public virtual void OnSelectionChanged() { }
/************************************************************************************************************************/
/// Called by .
public virtual void OnEnable(int index)
{
_Index = index;
_FullAnimator = new(IsVisible);
_BodyAnimator = new(IsExpanded);
}
/// Called by .
public virtual void OnDisable() { }
/************************************************************************************************************************/
/// Draws the GUI for this tool.
public virtual void DoGUI()
{
var enabled = GUI.enabled;
_FullAnimator.target = IsVisible;
if (EditorGUILayout.BeginFadeGroup(_FullAnimator.faded))
{
GUILayout.BeginVertical(EditorStyles.helpBox);
DoHeaderGUI();
_BodyAnimator.target = IsExpanded;
if (EditorGUILayout.BeginFadeGroup(_BodyAnimator.faded))
{
var instructions = Instructions;
if (!string.IsNullOrEmpty(instructions))
EditorGUILayout.HelpBox(instructions, MessageType.Info);
DoBodyGUI();
}
EditorGUILayout.EndFadeGroup();
GUILayout.EndVertical();
}
EditorGUILayout.EndFadeGroup();
if (_FullAnimator.isAnimating || _BodyAnimator.isAnimating)
Repaint();
GUI.enabled = enabled;
}
/************************************************************************************************************************/
///
/// Draws the Header GUI for this tool which is displayed regardless of whether it is expanded or not.
///
public virtual void DoHeaderGUI()
{
var area = AnimancerGUI.LayoutSingleLineRect(AnimancerGUI.SpacingMode.BeforeAndAfter);
var click = GUI.Button(area, Name, EditorStyles.boldLabel);
area.xMin = area.xMax - area.height;
GUI.DrawTexture(area, HelpIcon);
if (click)
{
if (area.Contains(Event.current.mousePosition))
{
Application.OpenURL(HelpURL);
return;
}
else
{
IsExpanded = !IsExpanded;
}
}
}
/************************************************************************************************************************/
/// Draws the Body GUI for this tool which is only displayed while it is expanded.
public abstract void DoBodyGUI();
/************************************************************************************************************************/
/// Asks the user where they want to save a modified asset, calls `modify` on it, and saves it.
public static bool SaveModifiedAsset(string saveTitle, string saveMessage,
T obj, Action modify) where T : Object
{
var originalPath = AssetDatabase.GetAssetPath(obj);
var extension = Path.GetExtension(originalPath);
if (extension[0] == '.')
extension = extension[1..];
var directory = Path.GetDirectoryName(originalPath);
var newName = Path.GetFileNameWithoutExtension(AssetDatabase.GenerateUniqueAssetPath(originalPath));
var savePath = EditorUtility.SaveFilePanelInProject(saveTitle, newName, extension, saveMessage, directory);
if (string.IsNullOrEmpty(savePath))
return false;
if (originalPath != savePath)
{
obj = Instantiate(obj);
AssetDatabase.CreateAsset(obj, savePath);
}
modify(obj);
AssetDatabase.SaveAssets();
return true;
}
/************************************************************************************************************************/
private static Texture _HelpIcon;
/// The help icon image used in the tool header.
public static Texture HelpIcon
{
get
{
if (_HelpIcon == null)
_HelpIcon = AnimancerIcons.Load("_Help");
return _HelpIcon;
}
}
/************************************************************************************************************************/
/// Adds any objects dropped in the `area` to the `list`.
protected void HandleDragAndDropIntoList(
Rect area,
IList list,
bool overwrite)
where T : Object
{
var dropIndex = 0;
// No easy way to avoid this closure.
AnimancerGUI.Handle((obj, isDrop) =>
{
if (!isDrop)
return true;
if (overwrite)
{
RecordUndo();
if (dropIndex < list.Count)
{
list[dropIndex++] = obj;
}
else
{
list.Add(obj);
}
}
else
{
list.Add(obj);
}
return true;
}, area);
}
/************************************************************************************************************************/
}
}
}
#endif