401 lines
14 KiB
C#
401 lines
14 KiB
C#
|
|
using System;
|
|||
|
|
using System.Collections.Generic;
|
|||
|
|
using System.Reflection;
|
|||
|
|
using UnityEditor;
|
|||
|
|
using UnityEditor.SceneManagement;
|
|||
|
|
using UnityEngine;
|
|||
|
|
using UObject = UnityEngine.Object;
|
|||
|
|
|
|||
|
|
namespace AlicizaX.EditorExtension.Editor
|
|||
|
|
{
|
|||
|
|
/// <summary>
|
|||
|
|
/// 通用表格绘制器
|
|||
|
|
/// </summary>
|
|||
|
|
public sealed class GenericTableWindow : EditorWindow
|
|||
|
|
{
|
|||
|
|
/// <summary>
|
|||
|
|
/// 打开通用表格绘制器
|
|||
|
|
/// </summary>
|
|||
|
|
/// <param name="target">表格数据目标实例</param>
|
|||
|
|
/// <param name="fieldName">表格数据的字段名称</param>
|
|||
|
|
public static void OpenWindow(UObject target, string fieldName)
|
|||
|
|
{
|
|||
|
|
GenericTableWindow window = GetWindow<GenericTableWindow>();
|
|||
|
|
window.titleContent.image = EditorGUIUtility.IconContent("ScriptableObject Icon").image;
|
|||
|
|
window.titleContent.text = "Generic Table";
|
|||
|
|
window.OnInit(target, fieldName);
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
private const int Border = 10;
|
|||
|
|
private const int TitleHeight = 20;
|
|||
|
|
private Dictionary<string, FieldInfo> _fieldInfos = new Dictionary<string, FieldInfo>();
|
|||
|
|
private TableView<object> _tableView;
|
|||
|
|
private UObject _target;
|
|||
|
|
private string _targetName;
|
|||
|
|
|
|||
|
|
|
|||
|
|
private void OnInit(UObject target, string fieldName)
|
|||
|
|
{
|
|||
|
|
FieldInfo fieldInfo = target.GetType().GetField(fieldName, BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic);
|
|||
|
|
if (fieldInfo == null)
|
|||
|
|
{
|
|||
|
|
Debug.LogWarning($"通用表格绘制器:未从 {target.GetType().FullName} 中找到字段 {fieldName}!");
|
|||
|
|
Close();
|
|||
|
|
return;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
List<object> datas = GetDatas(fieldInfo.GetValue(target));
|
|||
|
|
if (datas.Count <= 0)
|
|||
|
|
{
|
|||
|
|
Debug.LogWarning($"通用表格绘制器:{target.GetType().FullName} 的字段 {fieldName} 长度为0,或不是数组、集合类型!");
|
|||
|
|
Close();
|
|||
|
|
return;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
List<TableColumn<object>> columns = GetColumns(datas[0].GetType());
|
|||
|
|
if (columns.Count <= 0)
|
|||
|
|
{
|
|||
|
|
Debug.LogWarning($"通用表格绘制器:{target.GetType().FullName} 的字段 {fieldName} 不是复杂类型,或类型中不含有可序列化字段!");
|
|||
|
|
Close();
|
|||
|
|
return;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
_tableView = new TableView<object>(datas, columns);
|
|||
|
|
_tableView.IsEnableContextClick = false;
|
|||
|
|
_target = target;
|
|||
|
|
_targetName = $"{_target.GetType().FullName}.{fieldName} ({_target.name})";
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
private void OnGUI()
|
|||
|
|
{
|
|||
|
|
if (GUILayout.Button(_targetName, EditorStyles.toolbarButton))
|
|||
|
|
{
|
|||
|
|
Selection.activeObject = _target;
|
|||
|
|
EditorGUIUtility.PingObject(_target);
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
GUILayout.FlexibleSpace();
|
|||
|
|
|
|||
|
|
|
|||
|
|
Rect rect = new Rect(0, 0, position.width, position.height);
|
|||
|
|
rect.x += Border;
|
|||
|
|
rect.y += Border + TitleHeight;
|
|||
|
|
rect.width -= Border * 2;
|
|||
|
|
rect.height -= Border * 2 + TitleHeight;
|
|||
|
|
_tableView.OnGUI(rect);
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
private void Update()
|
|||
|
|
{
|
|||
|
|
if (EditorApplication.isCompiling || _tableView == null || _target == null)
|
|||
|
|
{
|
|||
|
|
Close();
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
private List<object> GetDatas(object field)
|
|||
|
|
{
|
|||
|
|
List<object> datas = new List<object>();
|
|||
|
|
Array array = field as Array;
|
|||
|
|
IEnumerable<object> list = field as IEnumerable<object>;
|
|||
|
|
if (array != null)
|
|||
|
|
{
|
|||
|
|
foreach (var item in array)
|
|||
|
|
{
|
|||
|
|
datas.Add(item);
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
else if (list != null)
|
|||
|
|
{
|
|||
|
|
foreach (var item in list)
|
|||
|
|
{
|
|||
|
|
datas.Add(item);
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
return datas;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
private List<TableColumn<object>> GetColumns(Type type)
|
|||
|
|
{
|
|||
|
|
_fieldInfos.Clear();
|
|||
|
|
FieldInfo[] fieldInfos = type.GetFields(BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic);
|
|||
|
|
for (int i = 0; i < fieldInfos.Length; i++)
|
|||
|
|
{
|
|||
|
|
if (fieldInfos[i].IsPublic || fieldInfos[i].IsDefined(typeof(SerializeField), true))
|
|||
|
|
{
|
|||
|
|
if (!_fieldInfos.ContainsKey(fieldInfos[i].Name))
|
|||
|
|
{
|
|||
|
|
_fieldInfos.Add(fieldInfos[i].Name, fieldInfos[i]);
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
List<TableColumn<object>> columns = new List<TableColumn<object>>();
|
|||
|
|
foreach (var item in _fieldInfos)
|
|||
|
|
{
|
|||
|
|
TableColumn<object> column = null;
|
|||
|
|
FieldInfo field = item.Value;
|
|||
|
|
if (field.FieldType.IsEnum)
|
|||
|
|
{
|
|||
|
|
column = GetEnumColumn(field);
|
|||
|
|
}
|
|||
|
|
else if (field.FieldType == typeof(string))
|
|||
|
|
{
|
|||
|
|
column = GetStringColumn(field);
|
|||
|
|
}
|
|||
|
|
else if (field.FieldType == typeof(int))
|
|||
|
|
{
|
|||
|
|
column = GetIntColumn(field);
|
|||
|
|
}
|
|||
|
|
else if (field.FieldType == typeof(float))
|
|||
|
|
{
|
|||
|
|
column = GetFloatColumn(field);
|
|||
|
|
}
|
|||
|
|
else if (field.FieldType == typeof(bool))
|
|||
|
|
{
|
|||
|
|
column = GetBoolColumn(field);
|
|||
|
|
}
|
|||
|
|
else if (field.FieldType == typeof(Vector2))
|
|||
|
|
{
|
|||
|
|
column = GetVector2Column(field);
|
|||
|
|
}
|
|||
|
|
else if (field.FieldType == typeof(Vector3))
|
|||
|
|
{
|
|||
|
|
column = GetVector3Column(field);
|
|||
|
|
}
|
|||
|
|
else if (field.FieldType == typeof(Color))
|
|||
|
|
{
|
|||
|
|
column = GetColorColumn(field);
|
|||
|
|
}
|
|||
|
|
else if (field.FieldType.IsSubclassOf(typeof(UObject)))
|
|||
|
|
{
|
|||
|
|
column = GetObjectColumn(field);
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
if (column != null)
|
|||
|
|
{
|
|||
|
|
column.autoResize = false;
|
|||
|
|
column.headerContent = new GUIContent(field.Name);
|
|||
|
|
columns.Add(column);
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
return columns;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
private TableColumn<object> GetEnumColumn(FieldInfo field)
|
|||
|
|
{
|
|||
|
|
TableColumn<object> column = new TableColumn<object>();
|
|||
|
|
column.width = 100;
|
|||
|
|
column.canSort = false;
|
|||
|
|
column.Compare = null;
|
|||
|
|
column.DrawCell = (rect, data, rowIndex, isSelected, isFocused) =>
|
|||
|
|
{
|
|||
|
|
EditorGUI.BeginChangeCheck();
|
|||
|
|
Enum value = EditorGUI.EnumPopup(rect, (Enum)field.GetValue(data));
|
|||
|
|
if (EditorGUI.EndChangeCheck())
|
|||
|
|
{
|
|||
|
|
field.SetValue(data, value);
|
|||
|
|
HasChanged(_target);
|
|||
|
|
}
|
|||
|
|
};
|
|||
|
|
return column;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
private TableColumn<object> GetStringColumn(FieldInfo field)
|
|||
|
|
{
|
|||
|
|
TableColumn<object> column = new TableColumn<object>();
|
|||
|
|
column.width = 100;
|
|||
|
|
column.canSort = true;
|
|||
|
|
column.Compare = (a, b) =>
|
|||
|
|
{
|
|||
|
|
string x = (string)field.GetValue(a);
|
|||
|
|
string y = (string)field.GetValue(b);
|
|||
|
|
return x.CompareTo(y);
|
|||
|
|
};
|
|||
|
|
column.DrawCell = (rect, data, rowIndex, isSelected, isFocused) =>
|
|||
|
|
{
|
|||
|
|
EditorGUI.BeginChangeCheck();
|
|||
|
|
string value = EditorGUI.TextField(rect, (string)field.GetValue(data));
|
|||
|
|
if (EditorGUI.EndChangeCheck())
|
|||
|
|
{
|
|||
|
|
field.SetValue(data, value);
|
|||
|
|
HasChanged(_target);
|
|||
|
|
}
|
|||
|
|
};
|
|||
|
|
return column;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
/// <summary>
|
|||
|
|
/// 标记目标已改变
|
|||
|
|
/// </summary>
|
|||
|
|
/// <param name="target">目标</param>
|
|||
|
|
protected void HasChanged(UnityEngine.Object target)
|
|||
|
|
{
|
|||
|
|
if (target != null)
|
|||
|
|
{
|
|||
|
|
EditorUtility.SetDirty(target);
|
|||
|
|
|
|||
|
|
if (EditorApplication.isPlaying)
|
|||
|
|
return;
|
|||
|
|
|
|||
|
|
GameObject gameObject = target as GameObject;
|
|||
|
|
if (gameObject != null && gameObject.scene != null)
|
|||
|
|
{
|
|||
|
|
EditorSceneManager.MarkSceneDirty(gameObject.scene);
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
Component component = target as Component;
|
|||
|
|
if (component != null && component.gameObject.scene != null)
|
|||
|
|
{
|
|||
|
|
EditorSceneManager.MarkSceneDirty(component.gameObject.scene);
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
private TableColumn<object> GetIntColumn(FieldInfo field)
|
|||
|
|
{
|
|||
|
|
TableColumn<object> column = new TableColumn<object>();
|
|||
|
|
column.width = 100;
|
|||
|
|
column.canSort = true;
|
|||
|
|
column.Compare = (a, b) =>
|
|||
|
|
{
|
|||
|
|
int x = (int)field.GetValue(a);
|
|||
|
|
int y = (int)field.GetValue(b);
|
|||
|
|
return x.CompareTo(y);
|
|||
|
|
};
|
|||
|
|
column.DrawCell = (rect, data, rowIndex, isSelected, isFocused) =>
|
|||
|
|
{
|
|||
|
|
EditorGUI.BeginChangeCheck();
|
|||
|
|
int value = EditorGUI.IntField(rect, (int)field.GetValue(data));
|
|||
|
|
if (EditorGUI.EndChangeCheck())
|
|||
|
|
{
|
|||
|
|
field.SetValue(data, value);
|
|||
|
|
HasChanged(_target);
|
|||
|
|
}
|
|||
|
|
};
|
|||
|
|
return column;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
private TableColumn<object> GetFloatColumn(FieldInfo field)
|
|||
|
|
{
|
|||
|
|
TableColumn<object> column = new TableColumn<object>();
|
|||
|
|
column.width = 100;
|
|||
|
|
column.canSort = true;
|
|||
|
|
column.Compare = (a, b) =>
|
|||
|
|
{
|
|||
|
|
float x = (float)field.GetValue(a);
|
|||
|
|
float y = (float)field.GetValue(b);
|
|||
|
|
return x.CompareTo(y);
|
|||
|
|
};
|
|||
|
|
column.DrawCell = (rect, data, rowIndex, isSelected, isFocused) =>
|
|||
|
|
{
|
|||
|
|
EditorGUI.BeginChangeCheck();
|
|||
|
|
float value = EditorGUI.FloatField(rect, (float)field.GetValue(data));
|
|||
|
|
if (EditorGUI.EndChangeCheck())
|
|||
|
|
{
|
|||
|
|
field.SetValue(data, value);
|
|||
|
|
HasChanged(_target);
|
|||
|
|
}
|
|||
|
|
};
|
|||
|
|
return column;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
private TableColumn<object> GetBoolColumn(FieldInfo field)
|
|||
|
|
{
|
|||
|
|
TableColumn<object> column = new TableColumn<object>();
|
|||
|
|
column.width = 40;
|
|||
|
|
column.canSort = false;
|
|||
|
|
column.Compare = null;
|
|||
|
|
column.DrawCell = (rect, data, rowIndex, isSelected, isFocused) =>
|
|||
|
|
{
|
|||
|
|
EditorGUI.BeginChangeCheck();
|
|||
|
|
bool value = EditorGUI.Toggle(rect, (bool)field.GetValue(data));
|
|||
|
|
if (EditorGUI.EndChangeCheck())
|
|||
|
|
{
|
|||
|
|
field.SetValue(data, value);
|
|||
|
|
HasChanged(_target);
|
|||
|
|
}
|
|||
|
|
};
|
|||
|
|
return column;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
private TableColumn<object> GetVector2Column(FieldInfo field)
|
|||
|
|
{
|
|||
|
|
TableColumn<object> column = new TableColumn<object>();
|
|||
|
|
column.width = 100;
|
|||
|
|
column.canSort = false;
|
|||
|
|
column.Compare = null;
|
|||
|
|
column.DrawCell = (rect, data, rowIndex, isSelected, isFocused) =>
|
|||
|
|
{
|
|||
|
|
EditorGUI.BeginChangeCheck();
|
|||
|
|
Vector2 value = EditorGUI.Vector2Field(rect, "", (Vector2)field.GetValue(data));
|
|||
|
|
if (EditorGUI.EndChangeCheck())
|
|||
|
|
{
|
|||
|
|
field.SetValue(data, value);
|
|||
|
|
HasChanged(_target);
|
|||
|
|
}
|
|||
|
|
};
|
|||
|
|
return column;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
private TableColumn<object> GetVector3Column(FieldInfo field)
|
|||
|
|
{
|
|||
|
|
TableColumn<object> column = new TableColumn<object>();
|
|||
|
|
column.width = 150;
|
|||
|
|
column.canSort = false;
|
|||
|
|
column.Compare = null;
|
|||
|
|
column.DrawCell = (rect, data, rowIndex, isSelected, isFocused) =>
|
|||
|
|
{
|
|||
|
|
EditorGUI.BeginChangeCheck();
|
|||
|
|
Vector3 value = EditorGUI.Vector3Field(rect, "", (Vector3)field.GetValue(data));
|
|||
|
|
if (EditorGUI.EndChangeCheck())
|
|||
|
|
{
|
|||
|
|
field.SetValue(data, value);
|
|||
|
|
HasChanged(_target);
|
|||
|
|
}
|
|||
|
|
};
|
|||
|
|
return column;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
private TableColumn<object> GetColorColumn(FieldInfo field)
|
|||
|
|
{
|
|||
|
|
TableColumn<object> column = new TableColumn<object>();
|
|||
|
|
column.width = 100;
|
|||
|
|
column.canSort = false;
|
|||
|
|
column.Compare = null;
|
|||
|
|
column.DrawCell = (rect, data, rowIndex, isSelected, isFocused) =>
|
|||
|
|
{
|
|||
|
|
EditorGUI.BeginChangeCheck();
|
|||
|
|
Color value = EditorGUI.ColorField(rect, (Color)field.GetValue(data));
|
|||
|
|
if (EditorGUI.EndChangeCheck())
|
|||
|
|
{
|
|||
|
|
field.SetValue(data, value);
|
|||
|
|
HasChanged(_target);
|
|||
|
|
}
|
|||
|
|
};
|
|||
|
|
return column;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
private TableColumn<object> GetObjectColumn(FieldInfo field)
|
|||
|
|
{
|
|||
|
|
TableColumn<object> column = new TableColumn<object>();
|
|||
|
|
column.width = 150;
|
|||
|
|
column.canSort = false;
|
|||
|
|
column.Compare = null;
|
|||
|
|
column.DrawCell = (rect, data, rowIndex, isSelected, isFocused) =>
|
|||
|
|
{
|
|||
|
|
EditorGUI.BeginChangeCheck();
|
|||
|
|
UObject value = EditorGUI.ObjectField(rect, field.GetValue(data) as UObject, field.FieldType, true);
|
|||
|
|
if (EditorGUI.EndChangeCheck())
|
|||
|
|
{
|
|||
|
|
field.SetValue(data, value);
|
|||
|
|
HasChanged(_target);
|
|||
|
|
}
|
|||
|
|
};
|
|||
|
|
return column;
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
}
|