Animator Extension: How to use advanced reflection step by step


This article shows how to use reflection to Open the Animator, Animation Window, and to Auto play the default animation of a gameObject, from the scene view.

This method does not require additionnal script on gameObject. It automaticly opens when you select a gameObject which contain an Animator !

I will try to show you how to use reflection step by step, with this exemple.

Demonstration

Here you see how easy it is to visualize an animation from the scene view.




How to Use


Simply Put these scripts inside an Editor/ Folder, and that's it !

AnimatorEditor
Download
Copy
/// <summary>
/// MIT License - Copyright(c) 2019 Ugo Belfiore
/// </summary>

using UnityEngine;
using UnityEditor;

namespace ExtUnityComponents
{
    [CustomEditor(typeof(Animator))]
    public class AnimatorEditor : DecoratorComponentsEditor
    {
        public AnimatorEditor()
                : base(editorTypeName:  BUILT_IN_EDITOR_COMPONENTS.AnimatorInspector,
                      showExtension:    false,
                      tinyEditorName:   "Animator Tool")
        {

        }

        /// <summary>
        /// get called by the decorator to show a tiny editor
        /// </summary>
        protected override void ShowTinyEditorContent()
        {
            if (GUILayout.Button("Play"))
            {
                ExtReflection.SetPlayButton();
            }
        }
    }
}
ExtReflection
Download
Copy
/// <summary>
/// MIT License - Copyright(c) 2019 Ugo Belfiore
/// </summary>

using UnityEngine;
using UnityEditor;
using System.Reflection;
using System;
using UnityEditorInternal;

namespace ExtUnityComponents
{
    /// <summary>
    /// useful reflection methods
    /// </summary>
    public class ExtReflection
    {
        /// <summary>
        /// play button on animator
        /// </summary>
        public static void SetPlayButton()
        {
            //open animator
            System.Type animatorWindowType = null;
            EditorWindow animatorWindow = OpenEditorWindow(AllNameAssemblyKnown.AnimatorControllerTool, ref animatorWindowType);

            //open animation
            System.Type animationWindowType = null;
            EditorWindow animationWindowEditor = OpenEditorWindow(AllNameAssemblyKnown.AnimationWindow, ref animationWindowType);

            //Get field m_AnimEditor
            FieldInfo animEditorFI = animationWindowType.GetField("m_AnimEditor", GetFullBinding());

            //Get the propertue of animEditorFI
            PropertyInfo controlInterfacePI = animEditorFI.FieldType.GetProperty("controlInterface", GetFullBinding());

            //Get property i splaying or not
            PropertyInfo isPlaying = controlInterfacePI.PropertyType.GetProperty("playing", GetFullBinding());

            //get object controlInterface
            object controlInterface = controlInterfacePI.GetValue(animEditorFI.GetValue(animationWindowEditor));
            //get the state of the "play" button
            bool playing = (bool)isPlaying.GetValue(controlInterface);

            if (!playing)
            {
                MethodInfo playMI = controlInterfacePI.PropertyType.GetMethod("StartPlayback", GetFullBinding());
                playMI.Invoke(controlInterface, new object[0]);
            }
            else
            {
                MethodInfo playMI = controlInterfacePI.PropertyType.GetMethod("StopPlayback", GetFullBinding());
                playMI.Invoke(controlInterface, new object[0]);
            }
        }

        /// <summary>
        /// for adding, do a GetAllEditorWindowTypes(true);
        /// </summary>
        public enum AllNameAssemblyKnown
        {
            BuildPlayerWindow,
            ConsoleWindow,
            IconSelector,
            ObjectSelector,
            ProjectBrowser,
            ProjectTemplateWindow,
            RagdollBuilder,
            SceneHierarchySortingWindow,
            SceneHierarchyWindow,
            ScriptableWizard,
            AddCurvesPopup,
            AnimationWindow,
            CurveEditorWindow,
            MinMaxCurveEditorWindow,
            AnnotationWindow,
            LayerVisibilityWindow,
            AssetStoreInstaBuyWindow,
            AssetStoreLoginWindow,
            AssetStoreWindow,
            AudioMixerWindow,
            CollabPublishDialog,
            CollabCannotPublishDialog,
            GameView,
            AboutWindow,
            AssetSaveDialog,
            BumpMapSettingsFixingWindow,
            ColorPicker,
            EditorUpdateWindow,
            FallbackEditorWindow,
            GradientPicker,
            PackageExport,
            PackageImport,
            PopupWindow,
            PopupWindowWithoutFocus,
            PragmaFixingWindow,
            SaveWindowLayout,
            DeleteWindowLayout,
            EditorToolWindow,
            SnapSettings,
            TreeViewTestWindow,
            GUIViewDebuggerWindow,
            InspectorWindow,
            PreviewWindow,
            AddShaderVariantWindow,
            AddComponentWindow,
            AdvancedDropdownWindow,
            LookDevView,
            AttachToPlayerPlayerIPWindow,
            HolographicEmulationWindow,
            FrameDebuggerWindow,
            SearchableEditorWindow,
            LightingExplorerWindow,
            LightingWindow,
            LightmapPreviewWindow,
            NavMeshEditorWindow,
            OcclusionCullingWindow,
            PhysicsDebugWindow,
            TierSettingsWindow,
            SceneView,
            SettingsWindow,
            ProjectSettingsWindow,
            PreferenceSettingsWindow,
            PackerWindow,
            SpriteUtilityWindow,
            TroubleshooterWindow,
            UIElementsEditorWindowCreator,
            UndoWindow,
            UnityConnectConsentView,
            UnityConnectEditorWindow,
            MetroCertificatePasswordWindow,
            MetroCreateTestCertificateWindow,
            WindowChange,
            WindowCheckoutFailure,
            WindowPending,
            WindowResolve,
            WindowRevert,
            WebViewEditorStaticWindow,
            WebViewEditorWindow,
            WebViewEditorWindowTabs,
            SearchWindow,
            LicenseManagementWindow,
            PackageManagerWindow,
            ParticleSystemWindow,
            PresetSelector,
            ProfilerWindow,
            UISystemPreviewWindow,
            ConflictResolverWindow,
            DeleteShortcutProfileWindow,
            PromptWindow,
            ShortcutManagerWindow,
            SketchUpImportDlg,
            TerrainWizard,
            ImportRawHeightmap,
            ExportRawHeightmap,
            TreeWizard,
            DetailMeshWizard,
            DetailTextureWizard,
            PlaceTreeWizard,
            FlattenHeightmap,
            TestEditorWindow,
            PanelDebugger,
            UIElementsDebugger,
            PainterSwitcherWindow,
            AllocatorDebugger,
            UIRDebugger,
            UIElementsSamples,
            AnimatorControllerTool,
            LayerSettingsWindow,
            ParameterControllerEditor,
            AddStateMachineBehaviourComponentWindow,
            AndroidKeystoreWindow,
            TimelineWindow,
            TMP_ProjectConversionUtility,
            TMP_SpriteAssetImporter,
            TMPro_FontAssetCreatorWindow,
            CollabHistoryWindow,
            CollabToolbarWindow,
            TestRunnerWindow,
            TMP_PackageResourceImporterWindow
        }

        public static System.Type GetEditorWindowTypeByName(string editorToFind, bool showDebug = false)
        {
            var result = new System.Collections.Generic.List<System.Type>();
            System.Reflection.Assembly[] AS = System.AppDomain.CurrentDomain.GetAssemblies();
            System.Type editorWindow = typeof(EditorWindow);
            foreach (var A in AS)
            {
                System.Type[] types = A.GetTypes();
                foreach (var T in types)
                {
                    if (T.IsSubclassOf(editorWindow))
                    {
                        if (showDebug)
                        {
                            Debug.Log(T.Name);
                        }
                        if (T.Name.Equals(editorToFind))
                        {
                            return (T);
                        }
                    }

                }
            }
            return (null);
        }

        public static FieldInfo[] GetAllFieldOfType(System.Type type, System.Reflection.BindingFlags bindings, bool showInConsol = false)
        {
            FieldInfo[] allField = type.GetFields(bindings);
            if (showInConsol)
            {
                for (int i = 0; i < allField.Length; i++)
                {
                    Debug.Log(allField[i].Name);
                }
            }
            return (allField);
        }

        /// <summary>
        /// Get all the most common Binding type of elements
        /// </summary>
        /// <returns></returns>
        public static System.Reflection.BindingFlags GetFullBinding()
        {
            return (BindingFlags.Public | BindingFlags.Instance | BindingFlags.DeclaredOnly | BindingFlags.NonPublic | BindingFlags.Static);
        }


        /// <summary>
        /// from a given name, Open an EditorWindow.
        /// you can also do:
        /// EditorApplication.ExecuteMenuItem("Window/General/Hierarchy");
        /// to open an known EditorWindow
        /// 
        /// To get the type of a known script, like UnityEditor.SceneHierarchyWindow, you can do also:
        /// System.Type type = typeof(EditorWindow).Assembly.GetType("UnityEditor.SceneHierarchyWindow");
        /// </summary>
        /// <param name="editorWindowName">name of the editorWindow to open</param>
        /// <param name="animationWindowType">type of the editorWindow (useful for others functions)</param>
        /// <returns></returns>
        public static EditorWindow OpenEditorWindow(AllNameAssemblyKnown editorWindowName, ref System.Type animationWindowType)
        {
            animationWindowType = GetEditorWindowTypeByName(editorWindowName.ToString());
            EditorWindow animatorWindow = EditorWindow.GetWindow(animationWindowType);
            return (animatorWindow);
        }
    }
}

DecoratorComponentsEditor
Download
Copy
/// <summary>
/// MIT License - Copyright(c) 2019 Ugo Belfiore
/// </summary>

using hedCommon.editor.editorWindow;
using hedCommon.extension.runtime;
using Sirenix.OdinInspector.Editor;
using System.Collections.Generic;
using System.Linq;
using System.Reflection;
using UnityEditor;
using UnityEngine;

namespace ExtUnityComponents
{
    public abstract class DecoratorComponentsEditor : Editor
    {
        /// <summary>
        /// all the virtual function at your disposal
        /// </summary>
        #region virtual functions

        /// <summary>
        /// called when the inspector is enabled
        /// </summary>
        public virtual void OnCustomEnable() { }

        /// <summary>
        /// called when the inspector is disabled
        /// </summary>
        public virtual void OnCustomDisable() { }


        /// <summary>
        /// called every OnInspectorGUI inside the herited class
        /// </summary>
        protected virtual void OnCustomInspectorGUI() { }

        /// <summary>
        /// called at the first OnInspectorGUI inside the herited class
        /// </summary>
        protected virtual void InitOnFirstInspectorGUI() { }

        /// <summary>
        /// called at the first OnSceneGUI inside the herited class
        /// </summary>
        /// <param name="sceneview">current drawing scene view</param>
        protected virtual void CustomOnSceneGUI(SceneView sceneview) { }

        /// <summary>
        /// called at the first OnSceneGUI inside the herited class
        /// </summary>
        /// <param name="sceneview">current drawing scene view</param>
        protected virtual void InitOnFirstOnSceneGUI(SceneView sceneview) { }

        /// <summary>
        /// called every editor Update inside the herited class
        /// </summary>
        protected virtual void OnEditorUpdate() { }

        /// <summary>
        /// called at the first Editor Update inside the herited class
        /// </summary>
        protected virtual void InitOnFirstEditorUpdate() { }

        public virtual void ShowTinyEditorContent() { }

        protected virtual void OnHierarchyChanged() { }


        #endregion

        /// <summary>
        /// some private variable
        /// </summary>
        #region private variables

        /// EditorPref key for save the open/close extension
        private const string KEY_EXPAND_EDITOR_EXTENSION = "KEY_EXPAND_EDITOR_EXTENSION";
        private bool _openExtensions = false;    //is this extension currently open or close ? save this for every gameObjects's components
        protected bool IsExtensionOpened() => _openExtensions;
        private readonly object[] EMPTY_ARRAY = new object[0];   //empty array for invoking methods using reflection
        private System.Type _decoratedEditorType = null;       //Type object for the internally used (decorated) editor.
        private System.Type _editedObjectType;          //Type object for the object that is edited by this editor.
        private Editor _editorInstance;                 //urrent editorInstance
        private bool _isCustomEditor = false;
        private string _nameCustomEditor;
        private bool _hasInitSceneGUI = false;
        private bool _hasInitEditorUpdate = false;
        private bool _hasInitInspector = false;
        private bool _showExtension = true;
        private bool _showTinyEditor = false;
        public TinyEditorWindowSceneView TinyEditorWindow;
        private readonly string KEY_EDITOR_PREF_EDITOR_WINDOW = "KEY_EDITOR_PREF_EDITOR_WINDOW";
        private string _tinyEditorName = "tiny Editor";
        public void ChangeNameEditor(string newName)
        {
            _tinyEditorName = newName;
            if (TinyEditorWindow != null)
            { 
                TinyEditorWindow.NameEditorWindow = newName;
            }
        }

        protected Component ConcretTarget;
        protected Component[] ConcretTargets;

        /// reflexion cache
        private Dictionary<string, MethodInfo> _decoratedMethods = new Dictionary<string, MethodInfo>();
        private Assembly _editorAssembly = Assembly.GetAssembly(typeof(Editor));

        /// <summary>
        /// get editor instance
        /// </summary>
        /// <returns>editor instance</returns>
        protected Editor GetEditorInstance()
        {
            return _editorInstance;
        }

        #endregion

        /// <summary>
        /// all the required setup and initialisation
        /// </summary>
        #region initialisation editor

        public DecoratorComponentsEditor(bool showExtension = false, string tinyEditorName = "")
        {
            SetupDecoratorSettings(showExtension, tinyEditorName, true, "");
        }

        /// <summary>
        /// constructor of the class: here we initialize reflexion variables
        /// </summary>
        /// <param name="editorTypeName">name of the component we inspect</param>
        public DecoratorComponentsEditor(BUILT_IN_EDITOR_COMPONENTS editorTypeName, bool showExtension = true, string tinyEditorName = "")
        {
            SetupDecoratorSettings(showExtension, tinyEditorName, isCustomEditor: false, editorTypeName.ToString());
            SetupCustomEditor(editorTypeName);
        }

        private void SetupDecoratorSettings(bool showExtension, string tinyEditorName, bool isCustomEditor, string nameEditor)
        {
            _showExtension = showExtension;
            _showTinyEditor = !string.IsNullOrEmpty(tinyEditorName);
            _isCustomEditor = isCustomEditor;
            _nameCustomEditor = nameEditor;
            _tinyEditorName = tinyEditorName;
        }

        private void InitTinyEditorWindow(string tinyEditorName)
        {
            TinyEditorWindow = new TinyEditorWindowSceneView();
            string key = KEY_EDITOR_PREF_EDITOR_WINDOW + this.GetType().ToString();
            TinyEditorWindow.TinyInit(key, tinyEditorName, TinyEditorWindowSceneView.DEFAULT_POSITION.UP_LEFT);
            TinyEditorWindow.IsClosable = false;
            TinyEditorWindow.IsMinimisable = true;
        }

        private void SetupCustomEditor(BUILT_IN_EDITOR_COMPONENTS editorTypeName)
        {
            _decoratedEditorType = _editorAssembly.GetTypes().Where(t => t.Name == editorTypeName.ToString()).FirstOrDefault();
            Init();

            // Check CustomEditor types.
            System.Type originalEditedType = GetCustomEditorType(_decoratedEditorType);

            if (originalEditedType != _editedObjectType)
            {
                throw new System.ArgumentException(
                    string.Format("Type {0} does not match the editor {1} type {2}",
                              _editedObjectType, editorTypeName, originalEditedType));
            }
        }

        /// <summary>
        /// need to be called here to initialize the default editor
        /// </summary>
        private void OnEnable()
        {
            SetupEditor();
            InitTargets();
            OnCustomEnable();
        }

        /// <summary>
        /// need to destroy the customEditor when quitting !
        /// </summary>
        private new void OnDisable()
        {
            if (_editorInstance != null)
            {
                DestroyImmediate(_editorInstance);
            }
            _hasInitSceneGUI = false;
            _hasInitEditorUpdate = false;
            _hasInitInspector = false;
            SceneView.duringSceneGui -= OwnOnSceneGUI;
            EditorApplication.update -= CustomEditorApplicationUpdate;
            EditorApplication.hierarchyChanged -= OnHierarchyChanged;
            OnCustomDisable();
        }

        public T GetTarget<T>() where T : Component
        {
            if (ConcretTarget == null)
            {
                return (default(T));
            }
            return ((T)ConcretTarget);
        }

        public GameObject GetGameObject()
        {
            return (ConcretTarget.gameObject);
        }
        public GameObject[] GetGameObjects()
        {
            if (ConcretTargets == null)
            {
                return (null);
            }
            GameObject[] gameObjects = new GameObject[ConcretTargets.Length];
            for (int i = 0; i < ConcretTargets.Length; i++)
            {
                gameObjects[i] = ConcretTargets[i].gameObject;
            }
            return (gameObjects);
        }

        public T[] GetTargets<T>() where T : Component
        {
            if (ConcretTarget == null)
            {
                return (default(T[]));
            }
            T[] array = new T[ConcretTargets.Length];
            for (int i = 0; i < ConcretTargets.Length; i++)
            {
                array[i] = (T)ConcretTargets[i];
            }
            return (array);
        }

        public T GetTargetIndex<T>(int index) where T : Component
        {
            if (ConcretTargets == null
                || ConcretTargets.Length == 0
                || index < 0
                || index >= ConcretTargets.Length)
            {
                return (default(T));
            }
            return ((T)ConcretTargets[index]);
        }

        /// <summary>
        /// return true if we have still the reference of the editor, but no more target...
        /// </summary>
        /// <returns></returns>
        public bool HaveWeLostTargets()
        {
            bool targetLost = ConcretTarget != null && target == null;
            bool concretTargetLost = ConcretTarget == null;
            bool contextLost = ConcretTarget != null && !Selection.gameObjects.Contains(ConcretTarget.gameObject);

            return (/*_isCustomEditor && */(targetLost || concretTargetLost || contextLost));
        }

        /// <summary>
        /// Need to destroy the instance even on Destroy !
        /// I don't know why. Unity generate leaks after each compilation
        /// because of the CreateEditor
        /// </summary>
        private void OnDestroy()
        {
            if (_editorInstance != null)
            {
                DestroyImmediate(_editorInstance);
            }
            //SceneView.duringSceneGui -= OwnOnSceneGUI;
            //EditorApplication.update -= OnEditorApplicationUpdate;
        }

        /// <summary>
        /// here create the editor instance, and open / close the extension panel if needed
        /// </summary>
        private void SetupEditor()
        {
            if (_editorInstance != null)
            {
                return;
            }
            _hasInitSceneGUI = false;
            _hasInitEditorUpdate = false;
            _hasInitInspector = false;
            CreateDefaultInstance();
            OpenOrCloseExtension();
        }


        /// <summary>
        /// here create the editor instance
        /// </summary>
        private void CreateDefaultInstance()
        {
            if (_decoratedEditorType == null || targets == null)
            {
                return;
            }
            _editorInstance = Editor.CreateEditor(targets, _decoratedEditorType);
        }

        /// <summary>
        /// manage if the extensions are opened or closed, and save this setting in EditorPrefs
        /// </summary>
        private void OpenOrCloseExtension()
        {
            if (EditorPrefs.HasKey(KEY_EXPAND_EDITOR_EXTENSION + _nameCustomEditor))
            {
                _openExtensions = EditorPrefs.GetBool(KEY_EXPAND_EDITOR_EXTENSION + _nameCustomEditor);
            }
            else
            {
                _openExtensions = false;
                EditorPrefs.SetBool(KEY_EXPAND_EDITOR_EXTENSION + _nameCustomEditor, _openExtensions);
            }
        }

       

        /// <summary>
        /// iniitalisation of reflexion variables
        /// </summary>
        private void Init()
        {
            var flags = BindingFlags.NonPublic | BindingFlags.Instance;

            var attributes = this.GetType().GetCustomAttributes(typeof(CustomEditor), true) as CustomEditor[];
            var field = attributes.Select(editor => editor.GetType().GetField("m_InspectedType", flags)).First();

            _editedObjectType = field.GetValue(attributes[0]) as System.Type;
        }

        /// <summary>
        /// from a given type of editor, get his CustomEditor as type
        /// </summary>
        /// <param name="type"></param>
        /// <returns></returns>
        private System.Type GetCustomEditorType(System.Type type)
        {
            var flags = BindingFlags.NonPublic | BindingFlags.Instance;

            var attributes = type.GetCustomAttributes(typeof(CustomEditor), true) as CustomEditor[];
            var field = attributes.Select(editor => editor.GetType().GetField("m_InspectedType", flags)).First();

            return field.GetValue(attributes[0]) as System.Type;
        }

        /// <summary>
        /// From a given name of class, invoke it using reflexion
        /// </summary>
        protected void CallMethodUsingReflexion(string methodName)
        {
            MethodInfo method = null;

            // Add MethodInfo to cache
            if (!_decoratedMethods.ContainsKey(methodName))
            {
                var flags = BindingFlags.Instance | BindingFlags.Static | BindingFlags.NonPublic | BindingFlags.Public;

                method = _decoratedEditorType.GetMethod(methodName, flags);

                if (method != null)
                {
                    _decoratedMethods[methodName] = method;
                }
                else
                {
                    //Debug.LogError(string.Format("Could not find method {0}", method));
                }
            }
            else
            {
                method = _decoratedMethods[methodName];
            }

            if (method != null)
            {
                method.Invoke(GetEditorInstance(), EMPTY_ARRAY);
            }
        }

        #endregion

        /// <summary>
        /// The core loop function for display
        /// everything
        /// </summary>
        #region Display Extension

        /// <summary>
        /// here the main InspectorGUI.
        /// It's here that we call the base OnInspecctorGUI.
        /// Then we show the GUI --------- Extension [+]
        /// then if it's open, we call the abstract class CustomOnInspectorGUI()
        /// </summary>
        public override void OnInspectorGUI()
        {
            if (!_isCustomEditor)
            {
                ManageUnityInspector();
            }
            else
            {
                ManageCustomNewInspector();
            }
        }
        private void ManageUnityInspector()
        {
            if (GetEditorInstance() == null)
            {
                return;
            }
            if (HaveWeLostTargets())
            {
                DestroyImmediate(GetEditorInstance());
                return;
            }

            GetEditorInstance().OnInspectorGUI();
            CommonExtenstionInspectorGUI();
        }
        private void ManageCustomNewInspector()
        {
            base.OnInspectorGUI();
            CommonExtenstionInspectorGUI();
        }

        /// <summary>
        /// called whatever this editor is inherited from a custom unity component,
        /// or just a standar sutom editor
        /// </summary>
        private void CommonExtenstionInspectorGUI()
        {
            TryToInitOnFirstInspectorGUI();



            if (_showExtension
                && ConcretTarget != null
                && !IsInPrefabsStage()
                && CanShowExpandFromInterfaceSettings()
                && ExpendBehavior())
            {
                OnCustomInspectorGUI();
            }
        }

        /// <summary>
        /// this function first check if the target have a IHaveDecoratorOnIt interface on it
        /// if we don't have one, return automaticly true
        /// (not every components have this decorator, like internal components or out own without the interface)
        /// 
        /// then return the settings we want
        /// </summary>
        /// <returns></returns>
        private bool CanShowExpandFromInterfaceSettings()
        {
            if (!HaveInterfaceSettings(out IHaveDecoratorOnIt haveDecorator))
            {
                return (true);
            }
            return (haveDecorator.ShowExtension);
        }

        /// <summary>
        /// this function first check if the target have a IHaveDecoratorOnIt interface on it
        /// if we don't have one, return automaticly true
        /// (not every components have this decorator, like internal components or out own without the interface)
        /// 
        /// then return the settings we want
        /// </summary>
        /// <returns></returns>
        private bool CanShowTinyEditorFromInterfaceSettings()
        {
            if (!HaveInterfaceSettings(out IHaveDecoratorOnIt haveDecorator))
            {
                return (true);
            }
            return (haveDecorator.ShowTinyEditorWindow);
        }

        /// <summary>
        /// this function check if the target have a IHaveDecoratorOnIt interface on it
        /// if we don't have one, return automaticly true
        /// (not every components have this decorator, like internal components or out own without the interface)
        /// 
        /// then return the settings we want
        /// </summary>
        /// <returns></returns>
        private bool HaveInterfaceSettings(out IHaveDecoratorOnIt haveDecorator)
        {
            haveDecorator = null;
            if (!_isCustomEditor)
            {
                return (false);
            }

            if (ConcretTarget == null)
            {
                return (false);
            }

            IHaveDecoratorOnIt[] AllInterfaceDecorator = ConcretTarget.GetComponents<IHaveDecoratorOnIt>();
            for (int i = 0; i < AllInterfaceDecorator.Length; i++)
            {
                if (AllInterfaceDecorator[i] == null)
                {
                    continue;
                }

                if (AllInterfaceDecorator[i].GetReferenceDecoratorsComponent() == ConcretTarget)
                {
                    haveDecorator = AllInterfaceDecorator[i];

                    bool targetHaveInterface = haveDecorator != null;
                    return (targetHaveInterface);
                }
            }

            return (false);
        }

        private void InitTargets()
        {
            if (ConcretTargets == null)
            {
                ConcretTarget = (Component)target;
                ConcretTargets = new Component[targets.Length];
                for (int i = 0; i < targets.Length; i++)
                {
                    ConcretTargets[i] = (Component)targets[i];
                }

                //_meshFilterHiddedTools = _concretTarget.GetOrAddComponent<MeshFilterHiddedTools>();

                SceneView.duringSceneGui -= OwnOnSceneGUI;
                SceneView.duringSceneGui += OwnOnSceneGUI;
                EditorApplication.update -= CustomEditorApplicationUpdate;
                EditorApplication.update += CustomEditorApplicationUpdate;

                EditorApplication.hierarchyChanged -= OnHierarchyChanged;
                EditorApplication.hierarchyChanged += OnHierarchyChanged;
            }
        }

        /// <summary>
        /// init the targets inspected and store them.
        /// This is called at the first OnInspectorGUI()
        /// Not in the constructor ! we don't have targets information in the constructor
        /// </summary>
        private void TryToInitOnFirstInspectorGUI()
        {
            if (!_hasInitInspector)
            {
                InitOnFirstInspectorGUI();
                _hasInitInspector = true;
            }
        }

        private void TryToInitOnFirstOnEditorUpdate()
        {
            if (!_hasInitEditorUpdate)
            {
                InitOnFirstEditorUpdate();
                _hasInitEditorUpdate = true;
            }
        }

        /// <summary>
        /// editor update loop
        /// </summary>
        private void CustomEditorApplicationUpdate()
        {
            if (!_isCustomEditor)
            {
                if (GetEditorInstance() == null)
                {
                    return;
                }
            }
            if (HaveWeLostTargets())
            {
                DestroyImmediate(GetEditorInstance());
                return;
            }

            if (!IsExtensionOpened())
            {
                return;
            }
            TryToInitOnFirstOnEditorUpdate();
            OnEditorUpdate();
        }

        /// <summary>
        /// This function is called at each OnSceneGUI to try to initialize it
        /// </summary>
        private void TryToInitOnFirstOnSceneGUI(SceneView sceneview)
        {
            if (!_hasInitSceneGUI)
            {
                if (_showTinyEditor)
                {
                    InitTinyEditorWindow(_tinyEditorName);
                }

                InitOnFirstOnSceneGUI(sceneview);
                _hasInitSceneGUI = true;
            }
        }

        /// <summary>
        /// return true if we are in a prefabs editing mode
        /// </summary>
        /// <returns></returns>
        private bool IsInPrefabsStage()
        {
            return (ExtPrefabs.IsEditingInPrefabMode(ConcretTarget.gameObject));
        }

        public void OwnOnSceneGUI(SceneView sceneview)
        {
            if (!_isCustomEditor)
            {
                if (GetEditorInstance() == null)
                {
                    return;
                }
            }
            if (HaveWeLostTargets())
            {
                DestroyImmediate(GetEditorInstance());
                return;
            }

            TryToInitOnFirstOnSceneGUI(sceneview);

            if (_showTinyEditor
                && !IsInPrefabsStage()
                && CanShowTinyEditorFromInterfaceSettings())
            {
                TinyEditorWindow.ShowEditorWindow(ShowTinyEditorContent, SceneView.currentDrawingSceneView, Event.current);
            }

            
            if (!IsExtensionOpened())
            {
                return;
            }
            

            CustomOnSceneGUI(sceneview);
        }

        /// <summary>
        /// display the Expand behavior of the component editor
        /// </summary>
        private bool ExpendBehavior()
        {
            GUIStyle lineStyle = new GUIStyle();
            lineStyle.normal.background = EditorGUIUtility.whiteTexture;
            lineStyle.stretchWidth = true;
            lineStyle.margin = new RectOffset(0, 0, 12, 18);

            var c = GUI.color;
            var p = GUILayoutUtility.GetRect(GUIContent.none, lineStyle, GUILayout.Height(1));
            p.width -= 120;
            if (Event.current.type == EventType.Repaint)
            {
                GUI.color = EditorGUIUtility.isProSkin ?
                    new Color(0.157f, 0.157f, 0.157f) : new Color(0.5f, 0.5f, 0.5f);
                lineStyle.Draw(p, false, false, false, false);
            }

            EditorGUI.LabelField(new Rect(p.xMax + 30, p.y - 7, 70, 25), "Extensions");

            GUI.color = Color.white;
            GUI.backgroundColor = Color.white;
            if (GUI.Button(new Rect(p.xMax + 100, p.y - 7, 18, 16), (_openExtensions ? " -" : " +")))
            {
                _openExtensions = !_openExtensions;
                EditorPrefs.SetBool(KEY_EXPAND_EDITOR_EXTENSION + _nameCustomEditor, _openExtensions);
            }
            GUI.backgroundColor = Color.white;


            GUI.color = c;

            return (_openExtensions);
        }

        
        #endregion

        /// <summary>
        /// other basic unity fonction we have to manage
        /// </summary>
        #region Unity Fonctions
        public void OnSceneGUI()
        {
            if (GetEditorInstance() == null)
            {
                return;
            }
            CallMethodUsingReflexion("OnSceneGUI");
        }

        protected override void OnHeaderGUI()
        {
            if (GetEditorInstance() == null)
            {
                return;
            }
            CallMethodUsingReflexion("OnHeaderGUI");
        }

        public override void DrawPreview(Rect previewArea)
        {
            if (GetEditorInstance() == null)
            {
                return;
            }
            GetEditorInstance().DrawPreview(previewArea);
        }

        public override string GetInfoString()
        {
            if (GetEditorInstance() == null)
            {
                return ("");
            }
            return GetEditorInstance().GetInfoString();
        }

        public override GUIContent GetPreviewTitle()
        {
            if (GetEditorInstance() == null)
            {
                return (null);
            }
            return GetEditorInstance().GetPreviewTitle();
        }

        public override bool HasPreviewGUI()
        {
            if (GetEditorInstance() == null)
            {
                return (false);
            }
            return GetEditorInstance().HasPreviewGUI();
        }

        public override void OnInteractivePreviewGUI(Rect r, GUIStyle background)
        {
            if (GetEditorInstance() == null)
            {
                return;
            }
            GetEditorInstance().OnInteractivePreviewGUI(r, background);
        }

        public override void OnPreviewGUI(Rect r, GUIStyle background)
        {
            if (GetEditorInstance() == null)
            {
                return;
            }
            GetEditorInstance().OnPreviewGUI(r, background);
        }

        public override void OnPreviewSettings()
        {
            if (GetEditorInstance() == null)
            {
                return;
            }
            GetEditorInstance().OnPreviewSettings();
        }

        public override void ReloadPreviewInstances()
        {
            if (GetEditorInstance() == null)
            {
                return;
            }
            GetEditorInstance().ReloadPreviewInstances();
        }

        public override Texture2D RenderStaticPreview(string assetPath, Object[] subAssets, int width, int height)
        {
            if (GetEditorInstance() == null)
            {
                return (null);
            }
            return GetEditorInstance().RenderStaticPreview(assetPath, subAssets, width, height);
        }

        public override bool RequiresConstantRepaint()
        {
            if (GetEditorInstance() == null)
            {
                return (false);
            }
            return GetEditorInstance().RequiresConstantRepaint();
        }

        public override bool UseDefaultMargins()
        {
            if (GetEditorInstance() == null)
            {
                return (true);
            }
            return GetEditorInstance().UseDefaultMargins();
        }

        #endregion
    }
}

BuiltInEditorComponentsEnum
Download
Copy
/// <summary>
/// MIT License - Copyright(c) 2019 Ugo Belfiore
/// </summary>

/// <summary>
/// this script containt the list of all 
/// Unity's Built In Inspector Types
/// 
/// Thanks to https://pastebin.com/Kq3T8aMw
/// for the datas
/// </summary>

namespace ExtUnityComponents
{
    public enum BUILT_IN_EDITOR_COMPONENTS
    {
        AnchoredJoint2DEditor,
        AnimationClipEditor,
        AnimationEditor,
        AnimatorInspector,
        AnimatorOverrideControllerInspector,
        AssetStoreAssetInspector,
        AudioChorusFilterEditor,
        AudioClipInspector,
        AudioDistortionFilterEditor,
        AudioEchoFilterEditor,
        AudioHighPassFilterEditor,
        AudioImporterInspector,
        AudioLowPassFilterInspector,
        AudioMixerControllerInspector,
        AudioMixerGroupEditor,
        AudioMixerSnapshotControllerInspector,
        AudioReverbFilterEditor,
        AudioReverbZoneEditor,
        AudioSourceInspector,
        AvatarEditor,
        AvatarMaskInspector,
        BillboardAssetInspector,
        BillboardRendererInspector,
        BlendTreeInspector,
        BoxCollider2DEditor,
        BoxColliderEditor,
        CameraEditor,
        CanvasEditor,
        CapsuleColliderEditor,
        CharacterControllerEditor,
        CircleCollider2DEditor,
        ClothInspector,
        Collider2DEditorBase,
        Collider3DEditorBase,
        ColliderEditorBase,
        ColorPresetLibraryEditor,
        CubemapInspector,
        CurvePresetLibraryEditor,
        DefaultAssetInspector,
        DistanceJoint2DEditor,
        DoubleCurvePresetLibraryEditor,
        EdgeCollider2DEditor,
        EditorSettingsInspector,
        Effector2DEditor,
        FogEditor,
        FontInspector,
        GameObjectInspector,
        GenericInspector,
        GradientPresetLibraryEditor,
        GraphicsSettingsInspector,
        HingeJoint2DEditor,
        HingeJointEditor,
        Joint2DEditorBase,
        LightEditor,
        LightingEditor,
        LightmapParametersEditor,
        LightProbeGroupInspector,
        LightProbesInspector,
        LineRendererInspector,
        LODGroupEditor,
        MaterialEditor,
        MeshColliderEditor,
        MeshRendererEditor,
        ModelImporterClipEditor,
        ModelImporterEditor,
        ModelImporterModelEditor,
        ModelImporterRigEditor,
        ModelInspector,
        MonoScriptImporterInspector,
        MonoScriptInspector,
        MovieImporterInspector,
        MovieTextureInspector,
        NavMeshAgentInspector,
        NavMeshObstacleInspector,
        OcclusionAreaEditor,
        OcclusionPortalInspector,
        OffMeshLinkInspector,
        OtherRenderingEditor,
        ParticleRendererEditor,
        ParticleSystemInspector,
        Physics2DSettingsInspector,
        PhysicsManagerInspector,
        PlayerSettingsEditor,
        PluginImporterInspector,
        PolygonCollider2DEditor,
        ProceduralMaterialInspector,
        ProceduralTextureInspector,
        QualitySettingsEditor,
        RectTransformEditor,
        ReflectionProbeEditor,
        RendererEditorBase,
        RenderSettingsInspector,
        RenderTextureInspector,
        RigidbodyEditor,
        ScriptExecutionOrderInspector,
        ShaderImporterInspector,
        ShaderInspector,
        ShaderVariantCollectionInspector,
        SkinnedMeshRendererEditor,
        SliderJoint2DEditor,
        SpeedTreeImporterInspector,
        SpeedTreeMaterialInspector,
        SphereColliderEditor,
        SpringJoint2DEditor,
        SpriteInspector,
        SpriteRendererEditor,
        SubstanceImporterInspector,
        TagManagerInspector,
        TerrainInspector,
        TextAssetInspector,
        TextMeshInspector,
        Texture3DInspector,
        TextureImporterInspector,
        TextureInspector,
        TrailRendererInspector,
        TransformInspector,
        TreeEditor,
        TrueTypeFontImporterInspector,
        WebCamTextureInspector,
        WheelColliderEditor,
        WheelJoint2DEditor,
        WindInspector
    }

    public enum CUSTOM_EDITOR_COMPONENTS
    {
        MeshFilterEditor
    }
}
TinyEditorWindowSceneView
Download
Copy
/// <summary>
/// MIT License - Copyright(c) 2019 Ugo Belfiore
/// </summary>

using System;
using System.Collections;
using System.Collections.Generic;
using UnityEditor;
using UnityEngine;

namespace ExtUnityComponents
{
    public class TinyEditorWindowSceneView
    {
        public struct SaveEditorPref
        {
            public Rect Position;
            public bool Minimised;
        }

        private SaveEditorPref _saveEditorPreference = new SaveEditorPref();

        public string GetJsonOfSave()
        {
            _saveEditorPreference.Position = _windowMenu;
            _saveEditorPreference.Minimised = _isMinimised;
            string json = JsonUtility.ToJson(_saveEditorPreference);
            return (json);
        }

        public SaveEditorPref GetFromJsonDatasEditorPref(string json)
        {
            _saveEditorPreference = JsonUtility.FromJson<SaveEditorPref>(json);
            return (_saveEditorPreference);
        }

        private Rect _windowMenu;

        public delegate void ContentToDrawDelegate();
        private ContentToDrawDelegate _methodToCall;
        private Event _currentEvent;
        private float _saveHeight = 0;
        private bool _isMinimised = false;

        private string _keyEditorPref = "";
        private string _nameEditorWindow = "";
        private int _id;

        private bool _draggable = false;
        public bool IsMinimisable = true;
        private Vector2 _lastPositionRight;
        private bool _snapRight;
        private bool _snapUp;
        public bool IsClosable = false;
        public EditorWindow _currentEditorWindow;
        private bool _lockToVisibleSizeX;    //if true, lock to the visible screen position, else, lock to the max size of the window
        private bool _lockToVisibleSizeY;    //if true, lock to the visible screen position, else, lock to the max size of the window

        private Rect _oldRect;
        private Vector2 _maxRect;  //determine the max value of size
        public bool IsClosed = false;

        public enum DEFAULT_POSITION
        {
            NONE = 0,

            UP_LEFT = 10,
            UP = 11,
            UP_RIGHT = 12,

            MIDDLE_LEFT = 20,
            MIDDLE = 21,
            MIDDLE_RIGHT = 22,

            DOWN_LEFT = 30,
            DOWN = 31,
            DOWN_RIGHT = 32,
        }

        /// <summary>
        /// 
        /// type:       UP_LEFT         UP          UP_RIGHT
        /// anchor:     (up left)       (up left)   (up right)
        /// 
        /// type:       MIDDLE_LEFT     MIDDLE      MIDDLE_RIGHT
        /// anchor:     (up left)       (up left)   (up right)
        /// 
        /// 
        /// type:       DOWN_LEFT       DOWN        DOWN_RIGHT
        /// anchor:     (down left)     (down left) (down right)
        /// </summary>
        /// <param name="position"></param>
        /// <param name="snapUp"></param>
        /// <param name="snapRight"></param>
        /// <returns></returns>
        public Rect GetNiceRectFromEnum(DEFAULT_POSITION position, float defaultWidth, out bool snapUp, out bool snapRight)
        {
            Rect rectNav = new Rect(0, 0, 0, 0);
            snapUp = true;
            snapRight = false;

            float height = 50;

            switch (position)
            {
                case DEFAULT_POSITION.UP_LEFT:
                    {
                        snapUp = true;
                        snapRight = false;

                        float x = 10;
                        float y = 10;
                        rectNav = new Rect(x, y, defaultWidth, height);
                        return (rectNav);
                    }
                case DEFAULT_POSITION.UP:
                    {
                        snapUp = true;
                        snapRight = false;

                        float x = (EditorGUIUtility.currentViewWidth / 2) - (defaultWidth / 2);
                        float y = 10;
                        rectNav = new Rect(x, y, defaultWidth, height);
                        return (rectNav);
                    }

                case DEFAULT_POSITION.UP_RIGHT:
                    {
                        snapUp = true;
                        snapRight = true;

                        float marginFromRight = 100f;
                        float x = EditorGUIUtility.currentViewWidth - defaultWidth - marginFromRight;
                        float y = 10;
                        rectNav = new Rect(x, y, defaultWidth, height);
                        return (rectNav);
                    }

                case DEFAULT_POSITION.MIDDLE_LEFT:
                    {
                        snapUp = true;
                        snapRight = false;

                        float x = 10f;
                        float y = (_currentEditorWindow.position.height / 2) - (height / 2);
                        rectNav = new Rect(x, y, defaultWidth, height);
                        return (rectNav);
                    }

                case DEFAULT_POSITION.MIDDLE_RIGHT:
                    {
                        snapUp = true;
                        snapRight = true;

                        float marginFromRight = 10f;
                        float x = EditorGUIUtility.currentViewWidth - defaultWidth - marginFromRight;
                        float y = (_currentEditorWindow.position.height / 2) - (height / 2);
                        rectNav = new Rect(x, y, defaultWidth, height);
                        return (rectNav);
                    }


                case DEFAULT_POSITION.DOWN_LEFT:
                    {
                        snapUp = false;
                        snapRight = false;

                        float marginFromDown = 10f;
                        float x = 10;
                        float y = _currentEditorWindow.position.height - height - marginFromDown;
                        rectNav = new Rect(x, y, defaultWidth, height);
                        return (rectNav);
                    }
                case DEFAULT_POSITION.DOWN:
                    {
                        float yFromDown = 10;  //from bottom, go upper
                        float x = (EditorGUIUtility.currentViewWidth / 2) - (defaultWidth / 2);
                        float y = SceneView.currentDrawingSceneView.camera.pixelRect.size.y - height - yFromDown;
                        rectNav = new Rect(x, y, defaultWidth, height);
                        return (rectNav);
                    }
                case DEFAULT_POSITION.DOWN_RIGHT:
                    {
                        float yFromDown = 10;  //from bottom, go upper
                        float xFromRight = 10;
                        float x = EditorGUIUtility.currentViewWidth - defaultWidth - xFromRight;
                        float y = SceneView.currentDrawingSceneView.camera.pixelRect.size.y - height - yFromDown;
                        rectNav = new Rect(x, y, defaultWidth, height);
                        return (rectNav);
                    }


                default:
                case DEFAULT_POSITION.MIDDLE:
                    {
                        snapUp = true;
                        snapRight = false;

                        float x = (EditorGUIUtility.currentViewWidth / 2) - (defaultWidth / 2);
                        float y = (_currentEditorWindow.position.height / 2) - (height / 2);
                        rectNav = new Rect(x, y, defaultWidth, height);
                        return (rectNav);
                    }
            }
        }

        public void TinyInit(string keyEditorPref, string nameEditorWindow, DEFAULT_POSITION position, float defaultWidth = 150)
        {
            _currentEditorWindow = SceneView.currentDrawingSceneView;

            Rect rect = GetNiceRectFromEnum(position, defaultWidth, out bool snapUp, out bool snapRight);

            Init(
                    keyEditorPref: keyEditorPref,
                    nameEditorWindow: nameEditorWindow,
                    id: keyEditorPref.GetHashCode(),
                    windowMenu: rect,
                    currentEditorWindow: _currentEditorWindow,
                    currentEvent: Event.current,
                    draggable: true,
                    isMinimisable: false,
                    snapRight: snapRight,
                    snapUp: snapUp,
                    maxRect: new Vector2(0, 0),
                    applySetup: true,
                    closable: true,
                    lockToVisibleSizeX: true,
                    lockToVisibleSizeY: true);
        }

        /// <summary>
        /// need to be called in OnGUI
        /// </summary>
        public void Init(string keyEditorPref,
                        string nameEditorWindow,
                        int id,

                        Rect windowMenu,
                        EditorWindow currentEditorWindow,
                        Event currentEvent,

                        bool draggable,
                        bool isMinimisable,
                        bool snapRight,
                        bool snapUp,

                        Vector2 maxRect,
                        bool applySetup = true,
                        bool closable = false,
                        bool lockToVisibleSizeX = true,
                        bool lockToVisibleSizeY = true)
        {
            _maxRect = maxRect;
            _lockToVisibleSizeX = lockToVisibleSizeX;
            _lockToVisibleSizeY = lockToVisibleSizeY;
            _keyEditorPref = keyEditorPref;
            _nameEditorWindow = nameEditorWindow;
            _id = id;
            _windowMenu = windowMenu;
            _currentEditorWindow = currentEditorWindow;
            _currentEvent = currentEvent;

            _draggable = draggable;
            IsMinimisable = isMinimisable;
            _snapRight = snapRight;
            _snapUp = snapUp;

            _isMinimised = false;
            _saveHeight = _windowMenu.height;
            IsClosable = closable;
            IsClosed = false;

            if (applySetup)
            {
                ApplySetup();
            }
        }

        public void SetNewPosition(Vector2 newPosition, Event currentEvent)
        {
            _currentEvent = currentEvent;
            _windowMenu.x = newPosition.x;
            _windowMenu.y = newPosition.y;
            EditorPrefs.SetString(_keyEditorPref, GetJsonOfSave());
        }

        public Rect GetPosition()
        {
            return (_windowMenu);
        }

        private void ApplySetup()
        {
            if (!string.IsNullOrEmpty(_keyEditorPref) && EditorPrefs.HasKey(_keyEditorPref))
            {
                ApplySaveSettings();
            }
            SavePositionFromRight(_snapRight, _snapUp);
        }

        /// <summary>
        /// apply the settings of the saved EditorPref
        /// </summary>
        private void ApplySaveSettings()
        {
            string keyRect = EditorPrefs.GetString(_keyEditorPref);
            _saveEditorPreference = GetFromJsonDatasEditorPref(keyRect);
            _windowMenu = new Rect(_saveEditorPreference.Position.x, _saveEditorPreference.Position.y, _windowMenu.width, _windowMenu.height);
            _isMinimised = _saveEditorPreference.Minimised;
            if (!IsMinimisable)
            {
                IsMinimisable = false;
            }
        }

        private bool HasChanged()
        {
            if (_saveEditorPreference.Position != _windowMenu
                || _saveEditorPreference.Minimised != _isMinimised)
            {
                return (true);
            }
            return (false);
        }

        private void SavePositionFromRight(bool snapRight, bool snapUp)
        {
            if (_currentEditorWindow == null)
            {
                _lastPositionRight = new Vector2(_windowMenu.x, _windowMenu.y);
                return;
            }

            float SnapWidth = (_lockToVisibleSizeX) ? EditorGUIUtility.currentViewWidth : _currentEditorWindow.position.height;
            float Snapheight = (_lockToVisibleSizeY) ? _currentEditorWindow.position.height : _currentEditorWindow.maxSize.y;

            _lastPositionRight.x = (snapRight) ? SnapWidth - _windowMenu.x : _windowMenu.x;
            _lastPositionRight.y = (snapUp) ? _windowMenu.y : Snapheight - _windowMenu.y;
        }

        private Vector2 GetNewRightPositiionFromOld(bool snapXRight, bool snapYUp)
        {
            if (_currentEditorWindow == null)
            {
                return (_lastPositionRight);
            }

            float SnapWidth = (_lockToVisibleSizeX) ? EditorGUIUtility.currentViewWidth : _currentEditorWindow.position.height;
            float Snapheight = (_lockToVisibleSizeY) ? _currentEditorWindow.position.height : _currentEditorWindow.maxSize.y;


            Vector2 pos;
            pos.x = (snapXRight) ? SnapWidth - _lastPositionRight.x : _lastPositionRight.x;
            pos.y = (snapYUp) ? _lastPositionRight.y : Snapheight - _lastPositionRight.y;
            return (pos);
        }

        /// <summary>
        /// called every frame, 
        /// </summary>
        /// <param name="action"></param>
        public Rect ShowEditorWindow(ContentToDrawDelegate action,
                                    EditorWindow currentEditorWindow,
                                    Event current)
        {

            _methodToCall = action;
            _currentEvent = current;
            _currentEditorWindow = currentEditorWindow;


            _windowMenu = ReworkPosition(_windowMenu, _snapRight, _snapUp);



            if (!IsMinimisable)
            {
                _isMinimised = false;
            }

            if (_isMinimised)
            {
                _windowMenu.height = 15f;
                _windowMenu = GUI.Window(_id, _windowMenu, NavigatorWindow, _nameEditorWindow);
                SavePositionFromRight(_snapRight, _snapUp);
            }
            else
            {
                //here resize auto
                _windowMenu = GUILayout.Window(_id, _windowMenu, NavigatorWindow, _nameEditorWindow, GUILayout.ExpandHeight(true));



                SavePositionFromRight(_snapRight, _snapUp);
            }

            if (!string.IsNullOrEmpty(_keyEditorPref) && HasChanged())
            {
                EditorPrefs.SetString(_keyEditorPref, GetJsonOfSave());
            }


            return (_windowMenu);
        }

        /// <summary>
        /// 
        /// </summary>
        /// <param name="minimise"></param>
        /// <param name="hard"></param>
        public void SetMinimise(bool minimise, bool hard)
        {
            if (!IsMinimisable)
            {
                if (!hard)
                {
                    return;
                }
                _isMinimised = minimise;
            }
            _isMinimised = minimise;
        }

        private void DisplayClosable()
        {
            float x = _windowMenu.width - 20;
            float y = 0;
            float width = 20f;
            float height = 15f;

            if (_maxRect.x > 0)
            {
                x = Mathf.Min(_maxRect.x, x);
            }
            if (_maxRect.y > 0)
            {
                y = Mathf.Min(_maxRect.y, y);
            }

            GUILayout.BeginArea(new Rect(x, y, width, height));
            if (GUILayout.Button(" x "))
            {
                IsClosed = true;
            }
            GUILayout.EndArea();
        }

        /// <summary>
        /// called to display the minimsie button
        /// </summary>
        private void DisplayMinimise()
        {
            if (_isMinimised)
            {
                GUILayout.BeginArea(new Rect(0, 0, _windowMenu.width, 15));
                GUILayout.Label("   " + _nameEditorWindow);
                GUILayout.EndArea();
            }


            float x = _windowMenu.width - 20;
            float y = 0;
            float width = 20f;
            float height = 15f;

            GUILayout.BeginArea(new Rect(x, y, width, height));
            bool minimise = GUILayout.Button(" - ");
            GUILayout.EndArea();

            if (minimise)
            {
                _isMinimised = (_isMinimised) ? false : true;
            }
        }

        private void DisplayDraggable()
        {
            GUILayout.BeginArea(new Rect(0, 0, 15, 15));
            GUILayout.Label("#");
            GUILayout.EndArea();
        }

        /// <summary>
        /// give restriction to position of the panel
        /// </summary>
        /// <param name="currentPosition"></param>
        /// <returns></returns>
        private Rect ReworkPosition(Rect currentPosition, bool snapRight, bool snapUp)
        {
            if (_currentEditorWindow == null)
            {
                return (currentPosition);
            }


            if (_maxRect.x > 0)
            {
                currentPosition.width = Mathf.Min(_maxRect.x, currentPosition.width);
            }
            if (_maxRect.y > 0)
            {
                currentPosition.height = Mathf.Min(_maxRect.y, currentPosition.height);
            }


            Vector2 snapToRight = GetNewRightPositiionFromOld(snapRight, snapUp);
            currentPosition.x = snapToRight.x;
            currentPosition.y = snapToRight.y;


            float minX = 10;
            float minY = 20;
            float maxX = EditorGUIUtility.currentViewWidth - currentPosition.width - minX;
            float maxY = _currentEditorWindow.position.height - currentPosition.height - 5;

            if (!_lockToVisibleSizeX)
            {
                maxX = _currentEditorWindow.maxSize.x - currentPosition.width - minX;
            }
            if (!_lockToVisibleSizeY)
            {
                maxY = _currentEditorWindow.maxSize.y - currentPosition.height - 5;
            }

            if (currentPosition.x < minX)
            {
                currentPosition.x = minX;
                _snapRight = false;
            }
            if (currentPosition.x > maxX)
            {
                currentPosition.x = maxX;
                _snapRight = true;
            }

            if (currentPosition.y < minY)
            {
                currentPosition.y = minY;
                _snapUp = true;
            }
            //here the _sceneView.camera.pixelRect.size.y is incoherent when applying this event
            if (_currentEvent.type == EventType.Repaint)
            {
                //Debug.Log(_currentEvent.type);
                if (currentPosition.y > maxY)
                {
                    currentPosition.y = maxY;
                    _snapUp = false;
                }
            }

            return (currentPosition);
        }


        /// <summary>
        /// draw the editor window with everything in it
        /// </summary>
        /// <param name="id"></param>
        private void NavigatorWindow(int id)
        {
            if (IsClosable)
            {
                DisplayClosable();
            }
            else if (IsMinimisable)
            {
                DisplayMinimise();
            }

            if (_draggable)
            {
                DisplayDraggable();
                GUI.DragWindow(new Rect(0, 0, _windowMenu.width - 20, 20));  //allow to drag de window only at the top
            }

            //don't draw anything if it's minimised
            if (_isMinimised)
            {
                return;
            }
            _methodToCall();
            PreventClicGoThought(Event.current);
        }

        public void PreventClicGoThought(Event current)
        {
            if ((current.type == EventType.MouseDrag || current.type == EventType.MouseDown)
                && current.button == 0)
            {
                Event.current.Use();
            }
        }
    }
}





How it works

If you are interested about how to do the custom editor which display the Tiny EditorWindow in the scene view, go check this devlog
If you are really curious about reflection, you can download the C# source code of Unity at their official github. From here you can open it in visual studio, and start to dig in.



Let's recapitulate the goal: Open the Animation and the Animator Window, then find and execute the "play" button of the Animation window.

Open an EditorWindow

The function
OpenEditorWindow()
will return an EditorWindow with a given name in parametter. It also returns the type, for convenience. With the code bellow, you get, and Open the EditorWinwow called
AnimationWindow
.


//open animation window
System.Type animationWindowType = null;
EditorWindow animationWindowEditor = OpenEditorWindow(AllNameAssemblyKnown.AnimationWindow, ref animationWindowType);


But wait, How did you know that the Animation Window was called "AnimationWindow" ?





Search AnimationWindow.cs script in the Unity source code

You can then search in the unity code to find the script associated:
In Visual studio with the UnityCsReference project open, Press CTRL+T and search for AnimationWindow.




Get a FieldInfo from a given System.Type

From the previous research in the Unity code, you can see this interesting line:
private AnimEditor m_AnimEditor;

Using reflection, you can get this variable as a FieldInfo.

Use
GetField()
to get the
m_AnimEditor
FieldInfo:
//Get field m_AnimEditor
FieldInfo animEditorFI = animationWindowType.GetField('m_AnimEditor', ExtReflectionion.GetFullBinding());

the function
GetFullBinding()
here is a little bit overkill. If you want to optimize, you could simply change it by
BindingFlags.NonPublic
(because m_AnimEditor is a private field)


But Wait again, How did you know that
m_AnimEditor
was the
FieldInfo
you were looking for ?





Get a PropertyInfo from a given FieldInfo

Press F12 on the variable AnimEditor
m_AnimEditor
to open the script in visual studio. Then, you can find the public
controlInterface
Propertie, which leads to the
IAnimationWindowControl.cs
script, with finaly, some interesting variables and functions !
Use
GetProperty()
to get the
controlInterface
PropertyInfo of the FieldInfo
animEditorFI.FieldType
Use
GetProperty()
to get the
playing
PropertyInfo of the PropertyInfo
controlInterfacePI.PropertyType
//Get the propertue of animEditorFI
PropertyInfo controlInterfacePI = animEditorFI.FieldType.GetProperty('controlInterface', ExtReflectionion.GetFullBinding());

//Get property i splaying or not
PropertyInfo isPlaying = controlInterfacePI.PropertyType.GetProperty('playing', ExtReflectionion.GetFullBinding());





Get the value of a PropertyInfo

It is not over yet ! with a PropertyInfo playing, you have to cast it into a boolean to test if the animation is playing or not:

//get object controlInterface
object controlInterface = controlInterfacePI.GetValue(animEditorFI.GetValue(animationWindowEditor));
//get the state of the 'play' button
bool playing = (bool)isPlaying.GetValue(controlInterface);
It is a complex process, it uses the previously found variables
controlInterfacePI
,
animEditorFI
and
animationWindowEditor
to get the value of
playing
:
public abstract bool playing { get; }





Call a method using MethodInfo

Finally ! you can test the boolean play, and call the right function. Here is how to call a function using MethodInfo:

MethodInfo playMI = controlInterfacePI.PropertyType.GetMethod('StartPlayback', ExtReflectionion.GetFullBinding());
playMI.Invoke(controlInterface, new object[0]);

That line will call the function:
public abstract bool StartPlayback();





And this is it ! A lot of code and thinking process for a little result ! It is the joy of reflection. I Hope I was clear about everything, if not, well, contact me anytime :).









See also:

Enhance any built-in components of unity

Add behavior of any unity components using customs inspectors and reflection


Change the mouse position by code in editor
(Only for tools purpose)

Save and load the position of the mouse inside unity (only in editor, for window)


Time, TimeScale and DeltaTime in Editor

I have created a TimeEditor Class which reflects the behaviour of the Time class in editor mode. Useful for tools who need timer & slow motion