Last active
May 23, 2023 12:28
-
-
Save AntonZhernosek/53711f3eda1b9284ef19d7a9632ce9ce to your computer and use it in GitHub Desktop.
UI Toolkit Dropdown Fields are annoying to deal with because some properties are kept internal to the UnityEditor assembly. Here are 2 simple extensions that allow you to fix it
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#if UNITY_EDITOR | |
using System; | |
using System.Linq.Expressions; | |
using System.Reflection; | |
using UnityEditor; | |
using UnityEngine.UIElements; | |
namespace Utilities.Extensions.UIToolkit | |
{ | |
public static class DropdownFieldExtensions | |
{ | |
private static Type genericOSMenuType; | |
private static Type iGenericMenuInterfaceType; | |
private const string CreateMenuCallback_Property_Name = "createMenuCallback"; | |
private const string GenericOsMenu_Type_Name = "UnityEditor.UIElements.GenericOSMenu"; | |
private const string IGenericMenu_Type_Name = "IGenericMenu"; | |
private const string FormatSelectedValueCallback_Property_Name = "formatSelectedValueCallback"; | |
#region Generic Menu Assigning | |
public static void AssignGenericMenu(this DropdownField dropdownField, | |
Func<GenericMenu> genericMenuBuildingFunc) | |
{ | |
Func<object> genericOSMenuFunc = () => | |
{ | |
GenericMenu menu = genericMenuBuildingFunc(); | |
return GetGenericOSMenu(menu); | |
}; | |
object boxedfunc = ConvertFuncToDesiredType(genericOSMenuFunc); | |
FieldInfo createMenuCallbackFieldInfo = typeof(DropdownField) | |
.GetField(CreateMenuCallback_Property_Name, BindingFlags.FlattenHierarchy | BindingFlags.NonPublic | BindingFlags.Public | BindingFlags.Instance); | |
createMenuCallbackFieldInfo.SetValue(dropdownField, boxedfunc); | |
} | |
#region Generic OS Menu Creation | |
private static object GetGenericOSMenu(GenericMenu menu) | |
{ | |
var type = GetGenericOSMenuType(); | |
object[] args = new object[] { menu }; | |
return Activator.CreateInstance(type, args); | |
} | |
#endregion | |
#region Type helpers | |
private static Type GetGenericOSMenuType() | |
{ | |
if (genericOSMenuType == null) | |
{ | |
genericOSMenuType = typeof(UnityEditor.UIElements.ColorField).Assembly.GetType(GenericOsMenu_Type_Name, false, true); | |
} | |
return genericOSMenuType; | |
} | |
private static Type GetIGenericMenuInterfaceType() | |
{ | |
if (iGenericMenuInterfaceType == null) | |
{ | |
var genericOSType = GetGenericOSMenuType(); | |
iGenericMenuInterfaceType = genericOSType.GetInterface(IGenericMenu_Type_Name); | |
} | |
return iGenericMenuInterfaceType; | |
} | |
#endregion | |
#region Func Conversion | |
private static Delegate ConvertFuncToDesiredType(Func<object> func) | |
{ | |
Type interfaceType = GetIGenericMenuInterfaceType(); | |
Type resultType = typeof(Func<>).MakeGenericType(interfaceType); | |
Expression<Func<object>> expressionFunc = FuncToExpression(func); | |
InvocationExpression invokedExpression = Expression.Invoke(expressionFunc); | |
UnaryExpression convertedReturnValue = Expression.Convert(invokedExpression, interfaceType); | |
LambdaExpression lambda = Expression.Lambda(delegateType: resultType, body: convertedReturnValue); | |
return lambda.Compile(); | |
} | |
private static Expression<Func<T>> FuncToExpression<T>(Func<T> f) | |
{ | |
return () => f(); | |
} | |
#endregion | |
#endregion | |
#region Formatting Callback Assigning | |
public static void AssignFormattingCallback(this DropdownField dropdownField, | |
Func<string, string> formattingCallback) | |
{ | |
#if UNITY_2022_2_OR_NEWER | |
dropdownField.formatSelectedValueCallback = formattingCallback; | |
#else | |
AssignFormattingCallbackViaReflection(dropdownField, formattingCallback); | |
#endif | |
} | |
private static void AssignFormattingCallbackViaReflection(this DropdownField functionDropdown, | |
Func<string, string> formattingCallback) | |
{ | |
PropertyInfo propertyInfo = typeof(DropdownField) | |
.GetProperty(FormatSelectedValueCallback_Property_Name, BindingFlags.FlattenHierarchy | BindingFlags.NonPublic | BindingFlags.Public | BindingFlags.Instance); | |
object[] args = new object[] { formattingCallback }; | |
propertyInfo.SetMethod.Invoke(functionDropdown, args); | |
} | |
#endregion | |
} | |
} | |
#endif |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment