Skip to content

Instantly share code, notes, and snippets.

@Arc-huangjingtong
Last active May 19, 2025 17:07
Show Gist options
  • Save Arc-huangjingtong/ddf510af71aea8dbce74a3c41b6ba5d6 to your computer and use it in GitHub Desktop.
Save Arc-huangjingtong/ddf510af71aea8dbce74a3c41b6ba5d6 to your computer and use it in GitHub Desktop.
UnityShowDictionaryInspectors
#if UNITY_EDITOR
using UnityEditor;
#endif
using System;
using System.Collections;
using System.Collections.Generic;
using System.Reflection;
using UnityEngine;
public class DictionaryDrawerAttribute : PropertyAttribute
{
public readonly string TargetDictName;
public DictionaryDrawerAttribute(string targetDictionaryName) => TargetDictName = targetDictionaryName;
}
#if UNITY_EDITOR
[CustomPropertyDrawer(typeof(DictionaryDrawerAttribute))]
public class ProxyDictionaryDrawer : PropertyDrawer
{
private static readonly Dictionary<string, bool> FoldoutStates = new();
private const float LineHeight = 20f;
private const float FoldoutWidth = 15f;
public override void OnGUI(Rect position, SerializedProperty property, GUIContent label)
{
if (attribute is not DictionaryDrawerAttribute attr) return;
var targetObject = property.serializedObject.targetObject;
var dictField = targetObject.GetType().GetField(attr.TargetDictName, BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic);
if (dictField == null || !typeof(IDictionary).IsAssignableFrom(dictField.FieldType))
{
EditorGUI.HelpBox(position, $"Dictionary '{attr.TargetDictName}' not found", MessageType.Error);
return;
}
label.text = attr.TargetDictName;
var dict = dictField.GetValue(targetObject) as IDictionary;
DrawDictionaryProxy(position, property, dict, label);
}
private void DrawDictionaryProxy(Rect position, SerializedProperty property, IDictionary dict, GUIContent label)
{
var stateKey = $"{property.propertyPath}_{property.serializedObject.GetHashCode()}";
FoldoutStates.TryAdd(stateKey, false);
var headerRect = new Rect(position.x, position.y, position.width, LineHeight);
FoldoutStates[stateKey] = EditorGUI.Foldout(headerRect, FoldoutStates[stateKey], label, true);
if (!FoldoutStates[stateKey]) return;
var y = position.y + LineHeight;
var index = 0;
try
{
foreach (DictionaryEntry entry in dict)
{
var lineRect = new Rect(position.x + FoldoutWidth, y, position.width - FoldoutWidth, LineHeight);
DrawDictionaryLine(lineRect, entry);
y += LineHeight;
}
}
catch (InvalidOperationException)
{
// 处理迭代过程中字典被修改的情况
}
}
private void DrawDictionaryLine(Rect rect, DictionaryEntry entry)
{
GUI.enabled = false;
var keyContent = entry.Key != null ? entry.Key.ToString() : "null";
var valueContent = entry.Value != null ? entry.Value.ToString() : "null";
EditorGUI.TextField(new Rect(rect.x, rect.y, rect.width / 2 - 5, LineHeight),
$"{keyContent}");
EditorGUI.TextField(new Rect(rect.x + rect.width / 2 + 5, rect.y, rect.width / 2 - 5, LineHeight),
$"{valueContent}");
GUI.enabled = true;
}
public override float GetPropertyHeight(SerializedProperty property, GUIContent label)
{
var attr = attribute as DictionaryDrawerAttribute;
if (attr == null) return LineHeight;
var targetObject = property.serializedObject.targetObject;
var dictField = targetObject.GetType().GetField(attr.TargetDictName,
BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic);
if (dictField?.GetValue(targetObject) is not IDictionary dict) return LineHeight;
var stateKey = $"{property.propertyPath}_{property.serializedObject.GetHashCode()}";
return FoldoutStates.TryGetValue(stateKey, out var expanded) && expanded
? LineHeight * (dict.Count + 1)
: LineHeight;
}
}
#endif
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment