Files
Cielonos/Packages/com.lunawolfstudios.scriptablesheets/Editor/Modules/Shared/Utilities/SerializedPropertyUtility.cs
SoulliesOfficial 7ee2894a63 整合SLSUtilities
2026-01-17 11:35:49 -05:00

825 lines
27 KiB
C#

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text.RegularExpressions;
using UnityEditor;
using UnityEditorInternal;
using UnityEngine;
using Object = UnityEngine.Object;
namespace LunaWolfStudiosEditor.ScriptableSheets.Shared
{
public static class SerializedPropertyUtility
{
public static readonly Object[] UnityBuiltInAssets;
private static readonly List<SerializedPropertyType> s_InputFieldPropertyTypes = new List<SerializedPropertyType>()
{
SerializedPropertyType.Integer,
SerializedPropertyType.Float,
SerializedPropertyType.String,
SerializedPropertyType.ArraySize,
SerializedPropertyType.Character,
};
// Some Object fields are marked as editable when they shouldn't be. This list helps catch those.
private static readonly List<string> s_ReadOnlyUnityFields = new List<string>() { UnityConstants.Field.Script };
// Unity has a duplicate StaticEditorFlags value for ContributeGI and LightmapStatic so we remove one of them.
private static readonly string[] s_StaticEditorFlags = Enum.GetNames(typeof(StaticEditorFlags)).Where(name => name != "LightmapStatic").ToArray();
private static Type s_StringTableCollectionType;
private static readonly GUIContent s_CreateButtonContent = EditorGUIUtility.IconContent("Toolbar Plus");
private const float CreateButtonWidth = 20f;
static SerializedPropertyUtility()
{
UnityBuiltInAssets = AssetDatabase.LoadAllAssetsAtPath(UnityConstants.Path.BuiltInExtra);
}
// Draw our own fields to eliminate unwanted header and text attributes that property field uses by default.
public static void DrawProperty(this SerializedProperty property, Rect propertyRect, Object rootObject, bool isCustomField, out bool arraySizeChanged, bool showReadOnly, AssetPreviewSettings previewSettings, string defaultNewAssetPath, Action<ScriptableObject, Type> onObjectCreated)
{
arraySizeChanged = false;
// There are some fields on other Unity Asset types that are inaccessible. So only draw our own custom fields.
if (isCustomField)
{
var propertyType = property.propertyType;
switch (propertyType)
{
case SerializedPropertyType.Integer:
propertyRect.height = EditorGUIUtility.singleLineHeight;
if (property.IsTypeInt())
{
property.intValue = EditorGUI.IntField(propertyRect, property.intValue);
}
else if (property.IsTypeLong())
{
if (!showReadOnly && property.propertyPath.EndsWith(UnityConstants.Field.TableEntryReferenceKeyId))
{
DrawTableEntryKeyDropdown(propertyRect, property);
}
else
{
property.longValue = EditorGUI.LongField(propertyRect, property.longValue);
}
}
#if UNITY_2022_1_OR_NEWER
else if (property.IsTypeUInt())
{
property.uintValue = DrawUtility.GUI.UIntField(propertyRect, property.uintValue);
}
else if (property.IsTypeULong())
{
property.ulongValue = DrawUtility.GUI.ULongField(propertyRect, property.ulongValue);
}
#endif
else
{
property.intValue = EditorGUI.IntField(propertyRect, property.intValue);
}
break;
case SerializedPropertyType.ArraySize:
propertyRect.height = EditorGUIUtility.singleLineHeight;
var previousValue = property.intValue;
property.intValue = EditorGUI.IntField(propertyRect, property.intValue);
if (previousValue != property.intValue)
{
arraySizeChanged = true;
}
break;
case SerializedPropertyType.Boolean:
propertyRect.height = EditorGUIUtility.singleLineHeight;
property.boolValue = DrawUtility.GUI.ToggleCenter(propertyRect, property.boolValue);
break;
case SerializedPropertyType.Float:
propertyRect.height = EditorGUIUtility.singleLineHeight;
if (property.IsTypeFloat())
{
property.floatValue = EditorGUI.FloatField(propertyRect, property.floatValue);
}
else if (property.IsTypeDouble())
{
property.doubleValue = EditorGUI.DoubleField(propertyRect, property.floatValue);
}
else
{
property.floatValue = EditorGUI.FloatField(propertyRect, property.floatValue);
}
break;
case SerializedPropertyType.String:
if (!showReadOnly && property.propertyPath.EndsWith(UnityConstants.Field.TableReferenceCollectionName))
{
DrawLocalizationTablePicker(propertyRect, property, previewSettings);
}
else
{
property.stringValue = EditorGUI.TextField(propertyRect, property.stringValue);
}
break;
case SerializedPropertyType.Color:
property.colorValue = EditorGUI.ColorField(propertyRect, GUIContent.none, property.colorValue, true, true, false);
break;
case SerializedPropertyType.ObjectReference:
// Only draw asset previews if the height is greater than 1.
if (propertyRect.height > EditorGUIUtility.singleLineHeight)
{
DrawUtility.TableAssetPreview(property.objectReferenceValue, propertyRect, previewSettings);
propertyRect.height = EditorGUIUtility.singleLineHeight;
}
if (!IsDefaultUnityEngineType(rootObject))
{
var objectType = ReflectionUtility.GetNestedFieldType(rootObject.GetType(), property.propertyPath);
if (objectType != null)
{
// If the Object is null and it's a ScriptableObject then give the option to create a new instance of it directly from here.
if (property.objectReferenceValue == null && typeof(ScriptableObject).IsAssignableFrom(objectType))
{
var objectRect = new Rect(propertyRect.x, propertyRect.y, propertyRect.width - CreateButtonWidth, propertyRect.height);
var plusRect = new Rect(objectRect.xMax, propertyRect.y, CreateButtonWidth, propertyRect.height);
property.objectReferenceValue = EditorGUI.ObjectField(objectRect, property.objectReferenceValue, objectType, false);
s_CreateButtonContent.tooltip = $"Create new {objectType.Name} and auto-assign";
if (GUI.Button(plusRect, s_CreateButtonContent))
{
var newObject = ScriptableObject.CreateInstance(objectType);
var guids = AssetDatabase.FindAssets($"t:{objectType.Name}");
var folderPath = defaultNewAssetPath;
// First check if any existing asset path exists.
if (guids != null && guids.Length > 0)
{
var fullPath = AssetDatabase.GUIDToAssetPath(guids[0]);
var newFolderPath = System.IO.Path.GetDirectoryName(fullPath);
// If they exist in a valid folder than use that. Otherwise use the default new asset path.
if (AssetDatabase.IsValidFolder(newFolderPath))
{
folderPath = newFolderPath;
}
}
var filename = $"New{objectType.Name}{UnityConstants.Extensions.Asset}";
var uniquePath = AssetDatabase.GenerateUniqueAssetPath($"{folderPath}/{filename}");
AssetDatabase.CreateAsset(newObject, uniquePath);
AssetDatabase.SaveAssets();
EditorGUIUtility.PingObject(newObject);
Selection.activeObject = newObject;
property.objectReferenceValue = newObject;
onObjectCreated?.Invoke(newObject, objectType);
}
}
else
{
property.objectReferenceValue = EditorGUI.ObjectField(propertyRect, property.objectReferenceValue, objectType, false);
}
}
else
{
EditorGUI.PropertyField(propertyRect, property, GUIContent.none, false);
}
}
else
{
EditorGUI.PropertyField(propertyRect, property, GUIContent.none, false);
}
break;
case SerializedPropertyType.LayerMask:
propertyRect.height = EditorGUIUtility.singleLineHeight;
property.intValue = EditorGUI.MaskField(propertyRect, GUIContent.none, property.intValue, InternalEditorUtility.layers);
break;
case SerializedPropertyType.Enum:
propertyRect.height = EditorGUIUtility.singleLineHeight;
if (!IsDefaultUnityEngineType(rootObject, out string fullTypeName) && (!fullTypeName.StartsWith(UnityConstants.Type.TMPro) || IsAlignmentProperty(property.propertyPath)) && property.TryGetEnumType(rootObject, out Type enumType))
{
if (enumType.HasFlagsAttribute())
{
// Remove none and bitwise combination values.
var filteredEnumNames = new List<string>();
foreach (var value in Enum.GetValues(enumType))
{
var intValue = (int) value;
if (intValue != 0 && (intValue & (intValue - 1)) == 0)
{
filteredEnumNames.Add(value.ToString());
}
}
property.intValue = EditorGUI.MaskField(propertyRect, GUIContent.none, property.intValue, filteredEnumNames.ToArray());
}
else
{
property.intValue = Convert.ToInt32(EditorGUI.EnumPopup(propertyRect, (Enum) Enum.ToObject(enumType, property.intValue)));
}
}
else
{
// Special case for UnityEngine.UI.Slider Component to remove extra space when rendering the Direction property.
if (fullTypeName == UnityConstants.Type.UnityEngineUISlider && property.propertyPath == UnityConstants.Field.Direction)
{
propertyRect.y -= 8;
}
EditorGUI.PropertyField(propertyRect, property, GUIContent.none, false);
}
break;
case SerializedPropertyType.Character:
propertyRect.height = EditorGUIUtility.singleLineHeight;
var stringValue = EditorGUI.TextField(propertyRect, ((char) property.intValue).ToString());
if (!string.IsNullOrEmpty(stringValue))
{
property.intValue = stringValue[0];
}
break;
case SerializedPropertyType.AnimationCurve:
property.animationCurveValue = EditorGUI.CurveField(propertyRect, property.animationCurveValue);
break;
case SerializedPropertyType.Gradient:
var internalPropertyHeight = EditorGUI.GetPropertyHeight(property, false);
if (internalPropertyHeight > EditorGUIUtility.singleLineHeight)
{
var gradientValue = property.GetGradientValue();
property.SetGradientValue(EditorGUI.GradientField(propertyRect, gradientValue));
}
else
{
// Workaround for Unity bug where EditorGUI.GradientField bleeds outside of its rect.
EditorGUI.PropertyField(propertyRect, property, GUIContent.none, false);
}
break;
case SerializedPropertyType.Generic:
default:
propertyRect.height = EditorGUIUtility.singleLineHeight;
EditorGUI.PropertyField(propertyRect, property, GUIContent.none, false);
break;
}
}
else
{
// Draw Unity layer and tag fields accordingly.
switch (property.propertyPath)
{
case UnityConstants.Field.Layer:
propertyRect.height = EditorGUIUtility.singleLineHeight;
property.intValue = EditorGUI.LayerField(propertyRect, property.intValue);
break;
case UnityConstants.Field.StaticEditorFlags:
propertyRect.height = EditorGUIUtility.singleLineHeight;
var newValue = EditorGUI.MaskField(propertyRect, property.intValue, s_StaticEditorFlags);
// Unity uses -1 to represent everything in a mask field, but StaticEditorFlags uses int.MaxValue to represent everything.
if (newValue <= -1)
{
newValue = int.MaxValue;
}
property.intValue = newValue;
break;
case UnityConstants.Field.Tag:
propertyRect.height = EditorGUIUtility.singleLineHeight;
property.stringValue = EditorGUI.TagField(propertyRect, property.stringValue);
break;
default:
if (property.propertyType == SerializedPropertyType.Boolean)
{
propertyRect.height = EditorGUIUtility.singleLineHeight;
property.boolValue = DrawUtility.GUI.ToggleCenter(propertyRect, property.boolValue);
}
else if (property.propertyType == SerializedPropertyType.String)
{
propertyRect.height = EditorGUIUtility.singleLineHeight;
property.stringValue = EditorGUI.TextField(propertyRect, property.stringValue);
}
else if (property.propertyType == SerializedPropertyType.ObjectReference)
{
// Only draw asset previews if the height is greater than 1.
if (propertyRect.height > EditorGUIUtility.singleLineHeight)
{
DrawUtility.TableAssetPreview(property.objectReferenceValue, propertyRect, previewSettings);
propertyRect.height = EditorGUIUtility.singleLineHeight;
}
EditorGUI.PropertyField(propertyRect, property, GUIContent.none, false);
}
else
{
propertyRect.height = EditorGUIUtility.singleLineHeight;
EditorGUI.PropertyField(propertyRect, property, GUIContent.none, false);
}
break;
}
}
}
public static SerializedPropertyType GetSheetsPropertyType(this SerializedProperty property, bool isScriptableObject)
{
if (isScriptableObject)
{
return property.propertyType;
}
// Handle special cases like Layers and Tags where Unity considers them int and string fields respectively.
switch (property.propertyPath)
{
case UnityConstants.Field.Layer:
return SerializedPropertyType.LayerMask;
case UnityConstants.Field.StaticEditorFlags:
case UnityConstants.Field.Tag:
return SerializedPropertyType.Enum;
default:
return property.propertyType;
}
}
public static string FriendlyPropertyPath(this SerializedProperty property)
{
return FriendlyPropertyPath(property.propertyPath, property.displayName);
}
public static string FriendlyPropertyPath(string propertyPath, string displayName)
{
var friendlyPath = propertyPath;
if (friendlyPath.Contains('.'))
{
friendlyPath = friendlyPath.Replace(UnityConstants.ArrayPropertyPath, "[");
friendlyPath = friendlyPath.Replace($".{UnityConstants.Field.Array}.{UnityConstants.Field.ArraySize}", $".{UnityConstants.Field.ArraySize}");
friendlyPath = friendlyPath.Replace("m_", string.Empty);
if (friendlyPath.Length > 1)
{
var lastIndex = friendlyPath.LastIndexOf('.');
if (lastIndex > 0)
{
var secondLastIndex = friendlyPath.LastIndexOf('.', lastIndex - 1);
if (secondLastIndex > -1)
{
friendlyPath = friendlyPath.Substring(secondLastIndex + 1);
}
else if (friendlyPath.Contains(']') && !friendlyPath.Contains("]."))
{
var lastSegment = friendlyPath.Substring(lastIndex + 1);
friendlyPath = lastSegment;
}
}
friendlyPath = friendlyPath.Replace('.', ' ').Trim();
friendlyPath = Regex.Replace(friendlyPath, @"<([^>]+)>k__BackingField", "$1");
friendlyPath = Regex.Replace(friendlyPath, @"(\p{Ll})(\p{Lu})", "$1 $2");
friendlyPath = Regex.Replace(friendlyPath, @"\b[a-z]", c => c.Value.ToUpper());
if (!string.IsNullOrEmpty(friendlyPath))
{
return friendlyPath;
}
}
}
return displayName;
}
// Needs to handle edge cases like:
// Sprite PPtr<$Sprite>
// PPtr<Material PPtr<Material>
public static string FriendlyType(string type)
{
var lastOpen = type.LastIndexOf('<');
var lastClose = type.LastIndexOf('>');
if (lastOpen != -1 && lastClose != -1 && lastOpen < lastClose)
{
var lastValue = type.Substring(lastOpen + 1, lastClose - lastOpen - 1);
return lastValue.Replace("$", string.Empty);
}
return type;
}
public static string FriendlyType(this SerializedProperty property)
{
return FriendlyType(property.type);
}
public static string GetFloatStringValue(this SerializedProperty property)
{
// We can't use SerializedProperty.numericType until 2022.1 so we can roll our own by checking type.
switch (property.type)
{
case UnityConstants.Type.Float:
return property.floatValue.ToString();
case UnityConstants.Type.Double:
return property.doubleValue.ToString();
default:
return property.floatValue.ToString();
}
}
public static string GetIntStringValue(this SerializedProperty property)
{
switch (property.type)
{
case UnityConstants.Type.Int:
return property.intValue.ToString();
case UnityConstants.Type.Long:
return property.longValue.ToString();
case UnityConstants.Type.UInt:
#if UNITY_2022_1_OR_NEWER
return property.uintValue.ToString();
#else
return property.intValue.ToString();
#endif
case UnityConstants.Type.ULong:
#if UNITY_2022_1_OR_NEWER
return property.ulongValue.ToString();
#else
return property.longValue.ToString();
#endif
default:
return property.intValue.ToString();
}
}
public static bool TrySetFloatValue(this SerializedProperty property, string value)
{
switch (property.type)
{
case UnityConstants.Type.Float:
if (float.TryParse(value, out float floatValue))
{
property.floatValue = floatValue;
return true;
}
else
{
return false;
}
case UnityConstants.Type.Double:
if (double.TryParse(value, out double doubleValue))
{
property.doubleValue = doubleValue;
return true;
}
else
{
return false;
}
default:
if (float.TryParse(value, out float defaultValue))
{
property.floatValue = defaultValue;
return true;
}
else
{
return false;
}
}
}
public static bool TrySetIntValue(this SerializedProperty property, string value)
{
switch (property.type)
{
case UnityConstants.Type.Int:
if (int.TryParse(value, out int intValue))
{
property.intValue = intValue;
return true;
}
else
{
return false;
}
case UnityConstants.Type.Long:
if (long.TryParse(value, out long longValue))
{
property.longValue = longValue;
return true;
}
else
{
return false;
}
#if UNITY_2022_1_OR_NEWER
case UnityConstants.Type.UInt:
if (uint.TryParse(value, out uint uintValue))
{
property.uintValue = uintValue;
return true;
}
else
{
return false;
}
case UnityConstants.Type.ULong:
if (ulong.TryParse(value, out ulong ulongValue))
{
property.ulongValue = ulongValue;
return true;
}
else
{
return false;
}
#endif
default:
if (int.TryParse(value, out int defaultValue))
{
property.intValue = defaultValue;
return true;
}
else
{
return false;
}
}
}
public static Gradient GetGradientValue(this SerializedProperty property)
{
#if UNITY_2022_1_OR_NEWER
return property.gradientValue;
#else
var propertyInfo = property.GetGradientPropertyInfo();
return propertyInfo?.GetValue(property, null) as Gradient;
#endif
}
public static void SetGradientValue(this SerializedProperty property, Gradient newValue)
{
#if UNITY_2022_1_OR_NEWER
property.gradientValue = newValue;
#else
var propertyInfo = property.GetGradientPropertyInfo();
propertyInfo?.SetValue(property, newValue);
#endif
}
#if !UNITY_2022_1_OR_NEWER
private static System.Reflection.PropertyInfo GetGradientPropertyInfo(this SerializedProperty property)
{
var propertyName = "gradientValue";
var bindingFlags = System.Reflection.BindingFlags.Public | System.Reflection.BindingFlags.NonPublic | System.Reflection.BindingFlags.Instance;
System.Reflection.PropertyInfo propertyInfo = typeof(SerializedProperty).GetProperty(propertyName, bindingFlags);
if (propertyInfo != null)
{
return propertyInfo;
}
else
{
Debug.LogWarning($"Unable to find property '{propertyName}' for {nameof(SerializedProperty)} at {property.propertyPath}.");
return null;
}
}
#endif
public static bool IsArraySizeOrElement(this SerializedProperty property)
{
return property.propertyType == SerializedPropertyType.ArraySize || property.IsArrayElement();
}
public static bool IsArrayElement(this SerializedProperty property)
{
return IsArrayElement(property.propertyPath);
}
public static bool IsArrayElement(string propertyPath)
{
return propertyPath.Contains('[');
}
public static bool IsAssetReference(this SerializedProperty property)
{
return property.type.Contains(UnityConstants.Type.AssetReference) && property.FindPropertyRelative(UnityConstants.Field.AssetRefGuid) != null;
}
public static bool IsInputFieldProperty(this SerializedProperty property, bool isScriptableObject)
{
var propertyType = property.GetSheetsPropertyType(isScriptableObject);
return s_InputFieldPropertyTypes.Contains(propertyType);
}
public static bool IsPropertyVisible(this SerializedProperty property, bool showArrays, bool showReadOnly, out bool isReadOnly)
{
var isArraySizeOrElement = property.IsArraySizeOrElement();
isReadOnly = property.IsReadOnly();
return (showArrays || !isArraySizeOrElement) && (showReadOnly || !isReadOnly) && !property.hasVisibleChildren
&& property.propertyType != SerializedPropertyType.Generic && property.propertyType != SerializedPropertyType.ManagedReference;
}
public static bool IsReadOnly(this SerializedProperty property)
{
return s_ReadOnlyUnityFields.Contains(property.name) || !property.editable;
}
public static bool IsTypeDouble(this SerializedProperty property)
{
return property.type == UnityConstants.Type.Double;
}
public static bool IsTypeFloat(this SerializedProperty property)
{
return property.type == UnityConstants.Type.Float;
}
public static bool IsTypeInt(this SerializedProperty property)
{
return property.type == UnityConstants.Type.Int;
}
public static bool IsTypeLong(this SerializedProperty property)
{
return property.type == UnityConstants.Type.Long;
}
public static bool IsTypeUInt(this SerializedProperty property)
{
return property.type == UnityConstants.Type.UInt;
}
public static bool IsTypeULong(this SerializedProperty property)
{
return property.type == UnityConstants.Type.ULong;
}
public static bool TryGetEnumType(this SerializedProperty property, Object rootObject, out Type enumType)
{
var enumPropertyPath = property.propertyPath;
enumType = ReflectionUtility.GetNestedFieldType(rootObject.GetType(), enumPropertyPath);
if (enumType != null && enumType.IsEnum)
{
return true;
}
else
{
Debug.LogWarning($"Property on {nameof(Object)} {rootObject.name} at path {enumPropertyPath} is not a valid type of enum.");
return false;
}
}
// Various default Unity Engine Components have different limitations on how backing fields are serialized and accessed.
private static bool IsDefaultUnityEngineType(Object rootObject)
{
return IsDefaultUnityEngineType(rootObject, out string fullTypeName);
}
private static bool IsDefaultUnityEngineType(Object rootObject, out string fullTypeName)
{
fullTypeName = rootObject.GetType().FullName;
return fullTypeName.StartsWith(UnityConstants.Type.UnityEngine);
}
// Handle special case for TMPro alignment enum properties.
private static bool IsAlignmentProperty(string propertyPath)
{
return propertyPath == UnityConstants.Field.HorizontalAlignment || propertyPath == UnityConstants.Field.VerticalAlignment || propertyPath == UnityConstants.Field.TextAlignment;
}
private static Type GetSharedTableCollectionType()
{
if (s_StringTableCollectionType == null)
{
s_StringTableCollectionType = Type.GetType($"{UnityConstants.Type.UnityLocalizationSharedTableData}, {UnityConstants.Type.UnityLocalization}");
}
return s_StringTableCollectionType;
}
// Get GUID from "GUID:xxxx" or return null.
private static string GetGuidFrom(string value)
{
if (string.IsNullOrEmpty(value))
{
return null;
}
if (!value.StartsWith(UnityConstants.Guid, StringComparison.OrdinalIgnoreCase))
{
return null;
}
return value.Substring(UnityConstants.Guid.Length);
}
private static void DrawLocalizationTablePicker(Rect rect, SerializedProperty property, AssetPreviewSettings previewSettings)
{
var sharedTableType = GetSharedTableCollectionType();
// If the localization package isn't installed then fallback to a simple text field.
if (sharedTableType == null)
{
property.stringValue = EditorGUI.TextField(rect, property.stringValue);
return;
}
Object currentValue = null;
var guid = GetGuidFrom(property.stringValue);
if (!string.IsNullOrEmpty(guid))
{
var path = AssetDatabase.GUIDToAssetPath(guid);
currentValue = AssetDatabase.LoadAssetAtPath(path, sharedTableType);
}
if (rect.height > EditorGUIUtility.singleLineHeight)
{
DrawUtility.TableAssetPreview(currentValue, rect, previewSettings);
rect.height = EditorGUIUtility.singleLineHeight;
}
var newValue = EditorGUI.ObjectField(rect, currentValue, sharedTableType, false);
if (currentValue != newValue)
{
if (newValue == null)
{
property.stringValue = string.Empty;
}
else
{
var path = AssetDatabase.GetAssetPath(newValue);
var newGuid = AssetDatabase.AssetPathToGUID(path);
property.stringValue = $"{UnityConstants.Guid}{newGuid}";
}
}
}
private static void DrawTableEntryKeyDropdown(Rect rect, SerializedProperty property)
{
var sharedTableType = GetSharedTableCollectionType();
// If the localization package isn't installed then fallback to a simple long field.
if (sharedTableType == null)
{
property.longValue = EditorGUI.LongField(rect, property.longValue);
return;
}
var tableRefPath = property.propertyPath.Replace(UnityConstants.Field.TableEntryReferenceKeyId, UnityConstants.Field.TableReferenceCollectionName);
var tableRefProp = property.serializedObject.FindProperty(tableRefPath);
var guid = GetGuidFrom(tableRefProp.stringValue);
if (string.IsNullOrEmpty(guid))
{
EditorGUI.LabelField(rect, "<No Table Selected>");
return;
}
var path = AssetDatabase.GUIDToAssetPath(guid);
var sharedTable = AssetDatabase.LoadAssetAtPath(path, sharedTableType);
if (sharedTable == null)
{
EditorGUI.LabelField(rect, "<Missing Table>");
return;
}
var sharedEntriesProp = sharedTableType.GetProperty(UnityConstants.Field.SharedTableDataEntries);
if (sharedEntriesProp == null)
{
EditorGUI.LabelField(rect, "<Entries Not Found>");
return;
}
var entries = sharedEntriesProp.GetValue(sharedTable) as IEnumerable<object>;
if (entries == null)
{
EditorGUI.LabelField(rect, "<No Entries>");
return;
}
var ids = new List<long>();
var labels = new List<string>();
var entryType = entries.GetType().GetGenericArguments()[0];
var idProp = entryType.GetProperty(UnityConstants.Field.SharedTableDataId);
var keyProp = entryType.GetProperty(UnityConstants.Field.SharedTableDataKey);
foreach (var entry in entries)
{
var id = (long) idProp.GetValue(entry);
ids.Add(id);
var key = (string) keyProp.GetValue(entry);
labels.Add(key);
}
if (ids.Count <= 0)
{
EditorGUI.LabelField(rect, "<Empty Table>");
return;
}
var currentIndex = ids.IndexOf(property.longValue);
var newIndex = EditorGUI.Popup(rect, currentIndex, labels.ToArray());
if (currentIndex != newIndex && newIndex >= 0 && newIndex < ids.Count)
{
property.longValue = ids[newIndex];
}
}
}
}