using System; using System.Collections.Generic; using System.Reflection; using UnityEditor; using UnityEditor.SceneManagement; using UnityEngine; using UObject = UnityEngine.Object; namespace AlicizaX.EditorExtension.Editor { /// /// 通用表格绘制器 /// public sealed class GenericTableWindow : EditorWindow { /// /// 打开通用表格绘制器 /// /// 表格数据目标实例 /// 表格数据的字段名称 public static void OpenWindow(UObject target, string fieldName) { GenericTableWindow window = GetWindow(); 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 _fieldInfos = new Dictionary(); private TableView _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 datas = GetDatas(fieldInfo.GetValue(target)); if (datas.Count <= 0) { Debug.LogWarning($"通用表格绘制器:{target.GetType().FullName} 的字段 {fieldName} 长度为0,或不是数组、集合类型!"); Close(); return; } List> columns = GetColumns(datas[0].GetType()); if (columns.Count <= 0) { Debug.LogWarning($"通用表格绘制器:{target.GetType().FullName} 的字段 {fieldName} 不是复杂类型,或类型中不含有可序列化字段!"); Close(); return; } _tableView = new TableView(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 GetDatas(object field) { List datas = new List(); Array array = field as Array; IEnumerable list = field as IEnumerable; 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> 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> columns = new List>(); foreach (var item in _fieldInfos) { TableColumn 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 GetEnumColumn(FieldInfo field) { TableColumn column = new TableColumn(); 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 GetStringColumn(FieldInfo field) { TableColumn column = new TableColumn(); 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; } /// /// 标记目标已改变 /// /// 目标 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 GetIntColumn(FieldInfo field) { TableColumn column = new TableColumn(); 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 GetFloatColumn(FieldInfo field) { TableColumn column = new TableColumn(); 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 GetBoolColumn(FieldInfo field) { TableColumn column = new TableColumn(); 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 GetVector2Column(FieldInfo field) { TableColumn column = new TableColumn(); 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 GetVector3Column(FieldInfo field) { TableColumn column = new TableColumn(); 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 GetColorColumn(FieldInfo field) { TableColumn column = new TableColumn(); 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 GetObjectColumn(FieldInfo field) { TableColumn column = new TableColumn(); 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; } } }