Not a member of Pastebin yet?
Sign Up,
it unlocks many cool features!
- using System.Collections.Generic;
- using System.Reflection;
- using UnityEditor;
- using UnityEditorInternal;
- using UnityEngine;
- using UnityEngine.Events;
- [CustomPropertyDrawer(typeof(UnityEvent))]
- public class CollapsableEventDrawer : UnityEventDrawer
- {
- private class Styles
- {
- public readonly GUIStyle preButton = "RL FooterButton";
- public GUIContent iconToolbarPlus = EditorGUIUtility.IconContent("Toolbar Plus", "|Add to list");
- }
- private const string CALLS_PROPERTY_PATH = "m_PersistentCalls.m_Calls";
- private const string GET_STATE_METHOD_NAME = "GetState";
- private const string REORDERABLE_LIST_FIELD_NAME = "m_ReorderableList";
- private const float BUTTON_WIDTH = 30;
- private const float BUTTON_SPACING = 30;
- private static readonly BindingFlags NON_PULIC_INSTANCE_FLAGS = BindingFlags.NonPublic | BindingFlags.Instance;
- // Points the field inside Sate which has the reorderable list.
- private static FieldInfo _stateReorderableListFieldInfo;
- // Points to the GetState method defined in UnityEventDrawer.
- private static MethodInfo _getStateMethod;
- // Cached array for using reflection
- private static object[] _getStateArgs = new object[1];
- // A class that contains all the custom styling we need
- private static Styles _styles;
- // True if we have persistent calls and false if we don't.
- private bool _hasPersistentCalls;
- // Holds all our reorderable lists that belong to our state
- private IDictionary<State, ReorderableList> _lists;
- /// <summary>
- /// Used to initialize our values.
- /// </summary>
- public CollapsableEventDrawer()
- {
- _lists = new Dictionary<State, ReorderableList>();
- }
- /// <summary>
- /// A wrapper around the GetState function that is private in <see cref="UnityEventDrawer"/>
- /// </summary>
- private State GetState(SerializedProperty property)
- {
- if (_getStateMethod == null)
- {
- _getStateMethod = typeof(UnityEventDrawer).GetMethod(GET_STATE_METHOD_NAME, NON_PULIC_INSTANCE_FLAGS);
- }
- _getStateArgs[0] = property;
- return (State)_getStateMethod.Invoke(this, _getStateArgs);
- }
- /// <summary>
- /// Tries to get the cached ReorderableList from the state.
- /// </summary>
- private ReorderableList GetList(SerializedProperty property)
- {
- State state = GetState(property);
- if(_lists.ContainsKey(state))
- {
- return _lists[state];
- }
- ReorderableList list = GetReorderableListFromState(state);
- _lists[state] = list;
- return list;
- }
- /// <summary>
- /// Returns back the number of calls that are currently in the Unity Event.
- /// </summary>
- private bool HasPersistentCalls(SerializedProperty property)
- {
- return property.FindPropertyRelative(CALLS_PROPERTY_PATH).arraySize > 0;
- }
- /// <summary>
- /// Returns to Unity to tell it how much space we should use to draw. If we
- /// have one element we only reserve 32 units. 16 for the element itself and
- /// 16 for spacing from the next element. If we any calls we let Unity
- /// draw the default.
- /// </summary>
- public override float GetPropertyHeight(SerializedProperty property, GUIContent label)
- {
- _hasPersistentCalls = HasPersistentCalls(property);
- ReorderableList list = GetList(property);
- if (!_hasPersistentCalls)
- {
- list.elementHeight = 0;
- list.displayAdd = false;
- list.displayRemove = false;
- return EditorGUIUtility.singleLineHeight * 2f;
- }
- list.elementHeight = 43;
- list.displayAdd = true;
- list.displayRemove = true;
- return base.GetPropertyHeight(property, label);
- }
- /// <summary>
- /// Used to draw our element override a bit of the Unity logic in the case
- /// of having no elements.
- /// </summary>
- public override void OnGUI(Rect position, SerializedProperty property, GUIContent label)
- {
- if(_styles == null)
- {
- _styles = new Styles();
- }
- base.OnGUI(position, property, label);
- if (!_hasPersistentCalls)
- {
- position.x += position.width - BUTTON_SPACING;
- position.width = BUTTON_WIDTH;
- if(GUI.Button(position, _styles.iconToolbarPlus, _styles.preButton))
- {
- State state = GetState(property);
- ReorderableList list = GetReorderableListFromState(state);
- list.onAddCallback(list);
- state.lastSelectedIndex = 0;
- }
- }
- }
- /// <summary>
- /// Gets the internal instance of the <see cref="ReorderableList"/> that exists
- /// in the state.
- /// </summary>
- private static ReorderableList GetReorderableListFromState(State state)
- {
- if (_stateReorderableListFieldInfo == null)
- {
- _stateReorderableListFieldInfo = typeof(State).GetField(REORDERABLE_LIST_FIELD_NAME, NON_PULIC_INSTANCE_FLAGS);
- }
- return (ReorderableList)_stateReorderableListFieldInfo.GetValue(state);
- }
- }
Add Comment
Please, Sign In to add comment