// 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