MOD!
This commit is contained in:
@@ -0,0 +1,70 @@
|
||||
#if UNITY_EDITOR
|
||||
using UnityEditor;
|
||||
#endif
|
||||
using UnityEngine;
|
||||
|
||||
namespace SLSFramework.General
|
||||
{
|
||||
[System.Serializable]
|
||||
public class InterfaceHolder<T> where T : class
|
||||
{
|
||||
[SerializeField] private MonoBehaviour value;
|
||||
|
||||
public T Value
|
||||
{
|
||||
get
|
||||
{
|
||||
if (value == null)
|
||||
{
|
||||
Debug.LogError("value is null");
|
||||
return null;
|
||||
}
|
||||
|
||||
T castValue = value as T;
|
||||
if (castValue == null)
|
||||
{
|
||||
Debug.LogError($"value cannot be cast to {typeof(T)}. It is of type {value.GetType()}");
|
||||
}
|
||||
|
||||
return castValue;
|
||||
}
|
||||
}
|
||||
|
||||
public InterfaceHolder(MonoBehaviour value)
|
||||
{
|
||||
this.value = value;
|
||||
}
|
||||
}
|
||||
|
||||
#if UNITY_EDITOR && !ODIN_INSPECTOR
|
||||
[CustomPropertyDrawer(typeof(InterfaceHolder<>))]
|
||||
public class InterfaceHolderDrawer : PropertyDrawer
|
||||
{
|
||||
public override void OnGUI(Rect position, SerializedProperty property, GUIContent label)
|
||||
{
|
||||
EditorGUI.BeginProperty(position, label, property);
|
||||
|
||||
SerializedProperty valueProperty = property.FindPropertyRelative("value");
|
||||
|
||||
EditorGUI.BeginChangeCheck();
|
||||
MonoBehaviour newValue = (MonoBehaviour)EditorGUI
|
||||
.ObjectField(position, label, valueProperty.objectReferenceValue,
|
||||
typeof(MonoBehaviour), true);
|
||||
|
||||
if (EditorGUI.EndChangeCheck())
|
||||
{
|
||||
if (newValue == null || newValue.GetComponent(fieldInfo.FieldType.GenericTypeArguments[0]) != null)
|
||||
{
|
||||
valueProperty.objectReferenceValue = newValue;
|
||||
}
|
||||
else
|
||||
{
|
||||
Debug.LogWarning($"Assigned object must implement interface {fieldInfo.FieldType.GenericTypeArguments[0].Name}");
|
||||
}
|
||||
}
|
||||
|
||||
EditorGUI.EndProperty();
|
||||
}
|
||||
}
|
||||
#endif
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 4351450027391954e85fea86db758c08
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,97 @@
|
||||
using System.Linq;
|
||||
using System.Collections.Generic;
|
||||
using UnityEngine;
|
||||
|
||||
namespace SLSFramework.General
|
||||
{
|
||||
[System.Serializable]
|
||||
public class SerializableDictionary<TKey, TValue> : Dictionary<TKey, TValue>, ISerializationCallbackReceiver
|
||||
{
|
||||
[SerializeField] private List<SerializedDictionaryKVPProps<TKey, TValue>> dictionaryList = new();
|
||||
[SerializeField] private float dividerPosProp = 0.5f;
|
||||
void ISerializationCallbackReceiver.OnBeforeSerialize()
|
||||
{
|
||||
foreach (var kvp in this)
|
||||
{
|
||||
if (dictionaryList.FirstOrDefault(value => this.Comparer.Equals(value.Key, kvp.Key))
|
||||
is SerializedDictionaryKVPProps<TKey, TValue> serializedKVP)
|
||||
{
|
||||
serializedKVP.Value = kvp.Value;
|
||||
}
|
||||
else
|
||||
{
|
||||
dictionaryList.Add(kvp);
|
||||
}
|
||||
}
|
||||
|
||||
dictionaryList.RemoveAll(value => ContainsKey(value.Key) == false);
|
||||
|
||||
for (int i = 0; i < dictionaryList.Count; i++)
|
||||
{
|
||||
dictionaryList[i].index = i;
|
||||
}
|
||||
}
|
||||
|
||||
void ISerializationCallbackReceiver.OnAfterDeserialize()
|
||||
{
|
||||
Clear();
|
||||
|
||||
dictionaryList.RemoveAll(r => r.Key == null);
|
||||
|
||||
foreach (var serializedKVP in dictionaryList)
|
||||
{
|
||||
if (!(serializedKVP.isKeyDuplicated = ContainsKey(serializedKVP.Key)))
|
||||
{
|
||||
Add(serializedKVP.Key, serializedKVP.Value);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public new TValue this[TKey key]
|
||||
{
|
||||
get
|
||||
{
|
||||
#if UNITY_EDITOR
|
||||
if (ContainsKey(key))
|
||||
{
|
||||
var duplicateKeysWithCount = dictionaryList.GroupBy(item => item.Key)
|
||||
.Where(group => group.Count() > 1)
|
||||
.Select(group => new { Key = group.Key, Count = group.Count() });
|
||||
|
||||
foreach (var duplicatedKey in duplicateKeysWithCount)
|
||||
{
|
||||
Debug.LogError($"Key '{duplicatedKey.Key}' is duplicated {duplicatedKey.Count} times in the dictionary.");
|
||||
}
|
||||
|
||||
return base[key];
|
||||
}
|
||||
else
|
||||
{
|
||||
Debug.LogError($"Key '{key}' not found in dictionary.");
|
||||
return default(TValue);
|
||||
}
|
||||
#else
|
||||
return base[key];
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
[System.Serializable]
|
||||
public class SerializedDictionaryKVPProps<TypeKey, TypeValue>
|
||||
{
|
||||
public TypeKey Key;
|
||||
public TypeValue Value;
|
||||
|
||||
public int index;
|
||||
public bool isKeyDuplicated;
|
||||
|
||||
public SerializedDictionaryKVPProps(TypeKey key, TypeValue value) { this.Key = key; this.Value = value; }
|
||||
|
||||
public static implicit operator SerializedDictionaryKVPProps<TypeKey, TypeValue>(KeyValuePair<TypeKey, TypeValue> kvp)
|
||||
=> new SerializedDictionaryKVPProps<TypeKey, TypeValue>(kvp.Key, kvp.Value);
|
||||
public static implicit operator KeyValuePair<TypeKey, TypeValue>(SerializedDictionaryKVPProps<TypeKey, TypeValue> kvp)
|
||||
=> new KeyValuePair<TypeKey, TypeValue>(kvp.Key, kvp.Value);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 08185d6eb814648ce9cdfca048e1611b
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,433 @@
|
||||
using UnityEngine;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
|
||||
#if UNITY_EDITOR
|
||||
using UnityEditor;
|
||||
using UnityEditorInternal;
|
||||
|
||||
namespace SLSFramework.General
|
||||
{
|
||||
[CustomPropertyDrawer(typeof(SerializableDictionary<,>), true)]
|
||||
public class SerializableDictionaryDrawer : PropertyDrawer
|
||||
{
|
||||
public override void OnGUI(Rect rect, SerializedProperty prop, GUIContent label)
|
||||
{
|
||||
var indentedRect = EditorGUI.IndentedRect(rect);
|
||||
|
||||
void Head()
|
||||
{
|
||||
var headerRect = indentedRect;
|
||||
headerRect.height = EditorGUIUtility.singleLineHeight;
|
||||
|
||||
void ExpandablePanel()
|
||||
{
|
||||
var fullHeaderRect = new Rect(headerRect);
|
||||
fullHeaderRect.x -= 17;
|
||||
fullHeaderRect.width += 34;
|
||||
|
||||
if (Event.current != null && fullHeaderRect.Contains(Event.current.mousePosition))
|
||||
{
|
||||
Color transparentGrey = new Color(0.4f, 0.4f, 0.4f, 0.4f);
|
||||
EditorGUI.DrawRect(fullHeaderRect, transparentGrey);
|
||||
}
|
||||
|
||||
GUI.color = Color.clear;
|
||||
|
||||
if (GUI.Button(new Rect(fullHeaderRect.x, fullHeaderRect.y, fullHeaderRect.width - 40,
|
||||
fullHeaderRect.height), ""))
|
||||
{
|
||||
prop.isExpanded = !prop.isExpanded;
|
||||
}
|
||||
|
||||
GUI.color = Color.white;
|
||||
|
||||
var triangleRect = rect;
|
||||
triangleRect.height = EditorGUIUtility.singleLineHeight;
|
||||
|
||||
EditorGUI.Foldout(triangleRect, prop.isExpanded, "");
|
||||
}
|
||||
|
||||
void DisplayName()
|
||||
{
|
||||
GUI.color = Color.white;
|
||||
|
||||
#if UNITY_2022_1_OR_NEWER
|
||||
var labelRect = headerRect;
|
||||
GUI.Label(labelRect, prop.displayName);
|
||||
#else
|
||||
GUI.Label(headerRect, prop.displayName);
|
||||
#endif
|
||||
|
||||
GUI.color = Color.white;
|
||||
GUI.skin.label.fontSize = 12;
|
||||
GUI.skin.label.fontStyle = FontStyle.Normal;
|
||||
GUI.skin.label.alignment = TextAnchor.MiddleLeft;
|
||||
}
|
||||
|
||||
void DuplicatedKeysWarning()
|
||||
{
|
||||
if (Event.current != null && Event.current.type != EventType.Repaint)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
var hasRepeated = false;
|
||||
var repeatedKeys = new List<string>();
|
||||
|
||||
for (int i = 0; i < dictionaryList.arraySize; i++)
|
||||
{
|
||||
SerializedProperty isKeyRepeatedProperty = dictionaryList.GetArrayElementAtIndex(i)
|
||||
.FindPropertyRelative("isKeyDuplicated");
|
||||
|
||||
if (isKeyRepeatedProperty.boolValue)
|
||||
{
|
||||
hasRepeated = true;
|
||||
SerializedProperty keyProperty = dictionaryList.GetArrayElementAtIndex(i).FindPropertyRelative("Key");
|
||||
string keyString = GetSerializedPropertyValueAsString(keyProperty);
|
||||
repeatedKeys.Add(keyString);
|
||||
}
|
||||
}
|
||||
|
||||
if (!hasRepeated)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
float with = GUI.skin.label.CalcSize(new GUIContent(prop.displayName)).x;
|
||||
headerRect.x += with + 35f;
|
||||
var warningRect = headerRect;
|
||||
Rect warningRectIcon = new Rect(headerRect.x - 18, headerRect.y, headerRect.width, headerRect.height);
|
||||
GUI.color = Color.white;
|
||||
GUI.Label(warningRectIcon, EditorGUIUtility.IconContent("console.erroricon"));
|
||||
GUI.color = new Color(1.0f, 0.443f, 0.443f);
|
||||
GUI.skin.label.fontStyle = FontStyle.Bold;
|
||||
GUI.Label(warningRect, "Duplicated keys: " + string.Join(", ", repeatedKeys));
|
||||
GUI.color = Color.white;
|
||||
GUI.skin.label.fontStyle = FontStyle.Normal;
|
||||
}
|
||||
|
||||
string GetSerializedPropertyValueAsString(SerializedProperty property)
|
||||
{
|
||||
switch (property.propertyType)
|
||||
{
|
||||
case SerializedPropertyType.Integer:
|
||||
return property.intValue.ToString();
|
||||
case SerializedPropertyType.Boolean:
|
||||
return property.boolValue.ToString();
|
||||
case SerializedPropertyType.Float:
|
||||
return property.floatValue.ToString();
|
||||
case SerializedPropertyType.String:
|
||||
return property.stringValue;
|
||||
default:
|
||||
return "(Unsupported Type)";
|
||||
}
|
||||
}
|
||||
|
||||
ExpandablePanel();
|
||||
DisplayName();
|
||||
DuplicatedKeysWarning();
|
||||
}
|
||||
|
||||
void List()
|
||||
{
|
||||
if (!prop.isExpanded)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
SetupList(prop);
|
||||
|
||||
float newHeight = indentedRect.height - EditorGUIUtility.singleLineHeight - 3;
|
||||
indentedRect.y += indentedRect.height - newHeight;
|
||||
indentedRect.height = newHeight;
|
||||
|
||||
reorderableList.DoList(indentedRect);
|
||||
}
|
||||
|
||||
SetupProps(prop);
|
||||
|
||||
Head();
|
||||
List();
|
||||
}
|
||||
|
||||
public override float GetPropertyHeight(SerializedProperty prop, GUIContent label)
|
||||
{
|
||||
SetupProps(prop);
|
||||
|
||||
var height = EditorGUIUtility.singleLineHeight;
|
||||
|
||||
if (prop.isExpanded)
|
||||
{
|
||||
SetupList(prop);
|
||||
height += reorderableList.GetHeight() + 5;
|
||||
}
|
||||
|
||||
return height;
|
||||
}
|
||||
|
||||
private float GetListElementHeight(int index)
|
||||
{
|
||||
if (index >= dictionaryList.arraySize) return 0;
|
||||
|
||||
var kvpProp = dictionaryList.GetArrayElementAtIndex(index);
|
||||
var keyProp = kvpProp.FindPropertyRelative("Key");
|
||||
var valueProp = kvpProp.FindPropertyRelative("Value");
|
||||
|
||||
float keyHeight = EditorGUI.GetPropertyHeight(keyProp, true);
|
||||
float valueHeight;
|
||||
|
||||
if (IsSingleLine(valueProp))
|
||||
{
|
||||
// 如果Value是单行,高度就是它自身的高度
|
||||
valueHeight = EditorGUI.GetPropertyHeight(valueProp, true);
|
||||
}
|
||||
else
|
||||
{
|
||||
// 如果Value是复杂类型,基础高度是标题行的高度
|
||||
valueHeight = EditorGUIUtility.singleLineHeight;
|
||||
|
||||
// 如果它被展开了,需要加上所有子属性的高度
|
||||
if (valueProp.isExpanded)
|
||||
{
|
||||
foreach (var child in GetChildren(valueProp))
|
||||
{
|
||||
valueHeight += EditorGUI.GetPropertyHeight(child, true) + EditorGUIUtility.standardVerticalSpacing;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 返回Key和Value中较高者的高度,并增加一点垂直间距
|
||||
return Mathf.Max(keyHeight, valueHeight) + EditorGUIUtility.standardVerticalSpacing;
|
||||
}
|
||||
|
||||
void DrawListElement(Rect rect, int index, bool isActive, bool isFocused)
|
||||
{
|
||||
if (index >= dictionaryList.arraySize) return;
|
||||
|
||||
var kvpProp = dictionaryList.GetArrayElementAtIndex(index);
|
||||
var keyProp = kvpProp.FindPropertyRelative("Key");
|
||||
var valueProp = kvpProp.FindPropertyRelative("Value");
|
||||
|
||||
// 为整个元素添加一点垂直内边距
|
||||
rect.y += 2;
|
||||
|
||||
// --- 区域计算 ---
|
||||
var dividerWidh = 6f;
|
||||
var dividerPosition = (dividerPosProp != null) ? dividerPosProp.floatValue : 0.3f;
|
||||
var fullRect = rect;
|
||||
fullRect.width -= 1;
|
||||
|
||||
var keyRect = fullRect;
|
||||
keyRect.width *= dividerPosition;
|
||||
keyRect.width -= dividerWidh / 2;
|
||||
|
||||
var valueRect = fullRect;
|
||||
valueRect.x += fullRect.width * dividerPosition;
|
||||
valueRect.width *= (1 - dividerPosition);
|
||||
valueRect.width -= dividerWidh / 2;
|
||||
|
||||
// --- 绘制Key (保持不变) ---
|
||||
// 确保Key的高度是单行,避免它被拉伸
|
||||
keyRect.height = EditorGUIUtility.singleLineHeight;
|
||||
EditorGUI.PropertyField(keyRect, keyProp, GUIContent.none, true);
|
||||
|
||||
// --- 核心修改:自定义绘制Value列 ---
|
||||
if (IsSingleLine(valueProp))
|
||||
{
|
||||
// 如果Value是单行,则正常绘制
|
||||
valueRect.height = EditorGUIUtility.singleLineHeight;
|
||||
EditorGUI.PropertyField(valueRect, valueProp, GUIContent.none, true);
|
||||
}
|
||||
else
|
||||
{
|
||||
// 如果Value是复杂类型,则自定义绘制
|
||||
var headerRect = new Rect(valueRect.x + 12, valueRect.y, valueRect.width, EditorGUIUtility.singleLineHeight);
|
||||
|
||||
// --- 问题1修复:分离绘制三角箭头和标题,增加间距 ---
|
||||
// 1. 先只绘制三角箭头
|
||||
valueProp.isExpanded = EditorGUI.Foldout(headerRect, valueProp.isExpanded, GUIContent.none, true);
|
||||
|
||||
// 2. 在箭头右侧留出空间后,再绘制标题
|
||||
var titleRect = new Rect(headerRect.x + 3, headerRect.y, headerRect.width - 15, headerRect.height);
|
||||
EditorGUI.LabelField(titleRect, valueProp.type, EditorStyles.boldLabel);
|
||||
|
||||
// 如果展开了,则在下方绘制所有子属性
|
||||
if (valueProp.isExpanded)
|
||||
{
|
||||
// --- 问题2修复:正确的循环绘制逻辑 ---
|
||||
var contentRect = new Rect(valueRect.x,
|
||||
valueRect.y + EditorGUIUtility.singleLineHeight + EditorGUIUtility.standardVerticalSpacing, valueRect.width,
|
||||
valueRect.height);
|
||||
|
||||
EditorGUI.indentLevel++;
|
||||
|
||||
foreach (var child in GetChildren(valueProp))
|
||||
{
|
||||
// 1. 获取子属性自身的高度
|
||||
var childHeight = EditorGUI.GetPropertyHeight(child, true);
|
||||
// 2. 为子属性创建一个精确的矩形区域
|
||||
var childRect = new Rect(contentRect.x, contentRect.y, contentRect.width, childHeight);
|
||||
|
||||
// 3. 在这个精确的区域内绘制子属性
|
||||
//EditorGUI.PropertyField(childRect, child, true);
|
||||
|
||||
// 4. 保留属性名称,拉长子属性的输入框
|
||||
if (child.hasVisibleChildren == false)
|
||||
{
|
||||
EditorGUI.LabelField(new Rect(childRect.x, childRect.y, EditorGUIUtility.labelWidth, childHeight), child.displayName);
|
||||
Rect valueFieldRect = new Rect(childRect.x + EditorGUIUtility.labelWidth / 2,
|
||||
childRect.y, childRect.width - EditorGUIUtility.labelWidth / 2, childHeight);
|
||||
EditorGUI.PropertyField(valueFieldRect, child, GUIContent.none);
|
||||
}
|
||||
else
|
||||
{
|
||||
EditorGUI.PropertyField(childRect, child, true);
|
||||
}
|
||||
|
||||
// 5. 将下一个绘制的Y坐标向下移动,为下一个属性留出空间
|
||||
contentRect.y += childHeight + EditorGUIUtility.standardVerticalSpacing;
|
||||
}
|
||||
|
||||
EditorGUI.indentLevel--;
|
||||
}
|
||||
}
|
||||
|
||||
// --- 绘制和处理分割线 (保持不变) ---
|
||||
Divider(rect, fullRect, dividerPosition, dividerWidh);
|
||||
}
|
||||
|
||||
void Divider(Rect originalRect, Rect fullRect, float dividerPosition, float dividerWidth)
|
||||
{
|
||||
Rect dividerRect = fullRect;
|
||||
dividerRect.x += fullRect.width * dividerPosition - dividerWidth / 2;
|
||||
dividerRect.width = dividerWidth;
|
||||
|
||||
EditorGUIUtility.AddCursorRect(dividerRect, MouseCursor.ResizeHorizontal);
|
||||
if (Event.current != null && dividerRect.Contains(Event.current.mousePosition))
|
||||
{
|
||||
if (Event.current.type == EventType.MouseDown)
|
||||
{
|
||||
//isDividerDragged = true;
|
||||
}
|
||||
else if (Event.current.type == EventType.MouseUp
|
||||
|| Event.current.type == EventType.MouseMove
|
||||
|| Event.current.type == EventType.MouseLeaveWindow)
|
||||
{
|
||||
isDividerDragged = false;
|
||||
}
|
||||
}
|
||||
|
||||
if (isDividerDragged && Event.current != null && Event.current.type == EventType.MouseDrag)
|
||||
{
|
||||
dividerPosProp.floatValue = Mathf.Clamp(dividerPosProp.floatValue + Event.current.delta.x / originalRect.width, .2f, .8f);
|
||||
}
|
||||
}
|
||||
|
||||
private void ShowDictIsEmptyMessage(Rect rect)
|
||||
{
|
||||
GUI.Label(rect, "Empty");
|
||||
}
|
||||
|
||||
private IEnumerable<SerializedProperty> GetChildren(SerializedProperty prop)
|
||||
{
|
||||
prop = prop.Copy();
|
||||
var endProp = prop.GetEndProperty();
|
||||
prop.NextVisible(true);
|
||||
while (!SerializedProperty.EqualContents(prop, endProp))
|
||||
{
|
||||
yield return prop;
|
||||
if (!prop.NextVisible(false))
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
private IEnumerable<SerializedProperty> GetChildren(SerializedProperty prop, bool enterVisibleGrandchildren)
|
||||
{
|
||||
prop = prop.Copy();
|
||||
|
||||
var startPath = prop.propertyPath;
|
||||
|
||||
var enterVisibleChildren = true;
|
||||
|
||||
while (prop.NextVisible(enterVisibleChildren) && prop.propertyPath.StartsWith(startPath))
|
||||
{
|
||||
yield return prop;
|
||||
enterVisibleChildren = enterVisibleGrandchildren;
|
||||
}
|
||||
}
|
||||
|
||||
private bool IsSingleLine(SerializedProperty prop)
|
||||
{
|
||||
return prop.propertyType != SerializedPropertyType.Generic || prop.hasVisibleChildren == false;
|
||||
}
|
||||
|
||||
private void SetupList(SerializedProperty prop)
|
||||
{
|
||||
if (reorderableList != null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
SetupProps(prop);
|
||||
|
||||
this.reorderableList = new ReorderableList(dictionaryList.serializedObject, dictionaryList, true, false, true, true);
|
||||
this.reorderableList.drawElementCallback = DrawListElement;
|
||||
this.reorderableList.elementHeightCallback = GetListElementHeight;
|
||||
this.reorderableList.drawNoneElementCallback = ShowDictIsEmptyMessage;
|
||||
}
|
||||
|
||||
private ReorderableList reorderableList;
|
||||
private bool isDividerDragged;
|
||||
|
||||
public void SetupProps(SerializedProperty prop)
|
||||
{
|
||||
if (this.property != null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
this.property = prop;
|
||||
this.dictionaryList = prop.FindPropertyRelative("dictionaryList");
|
||||
this.dividerPosProp = prop.FindPropertyRelative("dividerPosProp");
|
||||
|
||||
// 尝试获取字段上的 KeyWidthAttribute
|
||||
// 如果找到了该属性,则设置 dividerPosProp 的值
|
||||
if (fieldInfo.GetCustomAttributes(typeof(KeyWidthAttribute), true).FirstOrDefault() is KeyWidthAttribute keyWidthAttribute)
|
||||
{
|
||||
this.dividerPosProp.floatValue = keyWidthAttribute.WidthPercentage;
|
||||
}
|
||||
else
|
||||
{
|
||||
this.dividerPosProp.floatValue = 0.5f; // 默认值
|
||||
}
|
||||
}
|
||||
|
||||
private SerializedProperty property;
|
||||
private SerializedProperty dictionaryList;
|
||||
private SerializedProperty dividerPosProp;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
namespace SLSFramework.General
|
||||
{
|
||||
/// <summary>
|
||||
/// 用于指定 SerializableDictionary 抽屉中 Key 区域的宽度占比。
|
||||
/// </summary>
|
||||
[System.AttributeUsage(System.AttributeTargets.Field)]
|
||||
public class KeyWidthAttribute : PropertyAttribute
|
||||
{
|
||||
public readonly float WidthPercentage;
|
||||
|
||||
/// <summary>
|
||||
/// 设置 Key 区域的宽度占比。
|
||||
/// </summary>
|
||||
/// <param name="widthPercentage">一个0.1到0.9之间的浮点数,代表Key区域占总宽度的百分比。</param>
|
||||
public KeyWidthAttribute(float widthPercentage)
|
||||
{
|
||||
// 将值限制在一个合理的范围内,避免UI错乱
|
||||
WidthPercentage = Mathf.Clamp(widthPercentage, 0.1f, 0.9f);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: af44f85b3a51e40cb8b1285fb308b2a7
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,17 @@
|
||||
using UnityEngine;
|
||||
|
||||
namespace SLSFramework.General
|
||||
{
|
||||
[System.Serializable]
|
||||
public class UnityObjectWrapper<T> where T : class
|
||||
{
|
||||
[SerializeField] private Object value;
|
||||
|
||||
public T Value => value as T;
|
||||
|
||||
public UnityObjectWrapper(Object value)
|
||||
{
|
||||
this.value = value;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 36996041f1dde6b46942025e4519df17
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
Reference in New Issue
Block a user