Skip to content

Instantly share code, notes, and snippets.

@eddieparker
Last active May 24, 2023 15:30
Show Gist options
  • Save eddieparker/7bb1fd3b253ce039994b5d677e620978 to your computer and use it in GitHub Desktop.
Save eddieparker/7bb1fd3b253ce039994b5d677e620978 to your computer and use it in GitHub Desktop.
easy buttons property drawer version
using UnityEngine;
using UnityEditor;
using System.Reflection;
using System.Linq;
using EasyButtons;
using System;
using System.Collections.Generic;
using System.Collections;
public class EasyButtonsPropertyDrawer<T> : PropertyDrawer
{
// Needed to extract the index frop the property path that looks like foo.bar.myList[5];
static int GetIndexFromPropertyPath(string propertyPath)
{
return Convert.ToInt32(new string(propertyPath.Where(c => char.IsDigit(c)).ToArray()));
}
// Lazily return the objects this property is referencing.
// Only handles basic types, lists and arrays for now.
IEnumerable<System.Object> GetObjects(SerializedProperty property)
{
foreach(var targetObject in property.serializedObject.targetObjects)
{
var obj = this.fieldInfo.GetValue( targetObject);
if (obj.GetType().IsArray)
{
var index = GetIndexFromPropertyPath(property.propertyPath);
obj = ((T[])obj)[index];
}
else if(obj is IList)
{
var index = GetIndexFromPropertyPath(property.propertyPath);
IList asList = ((IList)obj);
obj = (T)( asList[index] );
}
yield return obj;
}
}
// Unity adds annoying padding at the start, and since we're using gui layouts we can just say we take up no space
// and let the normal layout junk do it's magic.
public override float GetPropertyHeight(SerializedProperty property, GUIContent label) { return -2f; }
public override void OnGUI(Rect position, SerializedProperty property, GUIContent label)
{
property.isExpanded = EditorGUILayout.Foldout(property.isExpanded, label);
if(property.isExpanded)
{
// Loop through all methods with no parameters
var methods = typeof(T)
.GetMethods(BindingFlags.Instance | BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic)
.Where(m => m.GetParameters().Length == 0);
foreach (var method in methods)
{
// Get the ButtonAttribute on the method (if any)
var ba = (ButtonAttribute)Attribute.GetCustomAttribute(method, typeof(ButtonAttribute));
if (ba != null)
{
// Determine whether the button should be enabled based on its mode
var wasEnabled = GUI.enabled;
GUI.enabled = ba.Mode == ButtonMode.AlwaysEnabled
|| (EditorApplication.isPlaying ? ba.Mode == ButtonMode.EnabledInPlayMode : ba.Mode == ButtonMode.DisabledInPlayMode);
if (((int)ba.Spacing & (int)ButtonSpacing.Before) != 0) EditorGUILayout.Space(10);
// Draw a button which invokes the method
var buttonName = String.IsNullOrEmpty(ba.Name) ? ObjectNames.NicifyVariableName(method.Name) : ba.Name;
using(new GUILayout.HorizontalScope())
{
GUILayout.Space(EditorGUI.indentLevel*30);
if (GUILayout.Button(buttonName))
{
foreach(var t in GetObjects(property))
{
method.Invoke(t, null);
}
}
}
if (((int)ba.Spacing & (int)ButtonSpacing.After) != 0) EditorGUILayout.Space(10);
GUI.enabled = wasEnabled;
}
}
EditorGUI.indentLevel++;
var propertyIterator = property.Copy();
var siblingProperty = property.Copy();
siblingProperty.Next(false);
bool bOnceThrough = false;
while( propertyIterator.NextVisible(!bOnceThrough) // First time through, enter children (!bOnceThrough), otherwise don't
&& !SerializedProperty.EqualContents(propertyIterator, siblingProperty) // We haven't entered a sibling
)
{
bOnceThrough = true;
// Default
EditorGUILayout.PropertyField(propertyIterator, includeChildren: true);
}
EditorGUI.indentLevel--;
}
}
}
// Users can implement the property drawer like this. Not as nice as
[CustomPropertyDrawer(typeof(MyCustomType))]
public class CCA_Parameters_PropertyDrawer : EasyButtonsPropertyDrawer<MyCustomType>
{
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment