ObjectCollectorEditor.cs 17 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418
  1. using XGame.Framework.ObjectCollection;
  2. using System;
  3. using System.Collections.Generic;
  4. using System.Linq;
  5. using System.Text.RegularExpressions;
  6. using UnityEditor;
  7. using UnityEditorInternal;
  8. using UnityEngine;
  9. using System.Reflection;
  10. namespace XGame.Editor.ObjectCollection
  11. {
  12. [CustomPropertyDrawer(typeof(ObjectCollector))]
  13. public partial class ObjectCollectorEditor : PropertyDrawer
  14. {
  15. private static BindingFlags flags = BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance;
  16. private ReorderableList collectList;
  17. private ReorderableList DrawsearchList;
  18. private List<string> searchList = new List<string>();
  19. private SerializedProperty keys;
  20. private SerializedProperty values;
  21. private SerializedProperty bindTypes;
  22. private ObjectCollector objectCollector;
  23. //搜索框
  24. private string m_InputSearchText;
  25. private GUIStyle TextFieldRoundEdge;
  26. private GUIStyle TextFieldRoundEdgeCancelButton;
  27. private GUIStyle TextFieldRoundEdgeCancelButtonEmpty;
  28. private GUIStyle TransparentTextField;
  29. private void SetData(SerializedProperty property)
  30. {
  31. var ob = property.serializedObject.targetObject;
  32. var selfFields = ob.GetType().GetFields(flags);
  33. var baseFields = ob.GetType().BaseType.GetFields(flags);
  34. var fields = selfFields.Concat(baseFields).ToArray();
  35. foreach (var field in fields)
  36. {
  37. if (field.FieldType == typeof(ObjectCollector))
  38. {
  39. objectCollector = field.GetValue(ob) as ObjectCollector;
  40. break;
  41. }
  42. }
  43. }
  44. private void OnInit(SerializedProperty property)
  45. {
  46. SetData(property);
  47. collectList.headerHeight = 0;
  48. DrawsearchList.headerHeight = 0;
  49. // 绘制
  50. collectList.drawElementCallback = (Rect rect, int index, bool selected, bool focused) =>
  51. {
  52. var key = keys.GetArrayElementAtIndex(index);
  53. var value = values.GetArrayElementAtIndex(index);
  54. var bindType = bindTypes.GetArrayElementAtIndex(index);
  55. EditorGUILayout.BeginHorizontal();
  56. GUILayout.Space(10);
  57. // 3等分,中间间隔10像素
  58. var copyRect = new Rect(rect.x, rect.y + 2, (rect.width - 20) / 3, EditorGUIUtility.singleLineHeight);
  59. if (GUI.Button(copyRect, "", "HelpBox"))
  60. {
  61. TextEditor textEditor = new TextEditor();
  62. textEditor.text = key.stringValue;
  63. textEditor.OnFocus();
  64. textEditor.Copy();
  65. Debug.Log($"序列化器Key复制成功!Key:{key.stringValue}");
  66. };
  67. EditorGUI.BeginDisabledGroup(true);
  68. //key
  69. var keyRect = copyRect;
  70. EditorGUI.TextField(keyRect, key.stringValue);
  71. EditorGUI.EndDisabledGroup();
  72. //value
  73. var valueRect = keyRect;
  74. valueRect.x += keyRect.width + 10;
  75. Type type = objectCollector.collectorType;
  76. var tmp = value.objectReferenceValue;
  77. value.objectReferenceValue = EditorGUI.ObjectField(valueRect, value.objectReferenceValue, type, true);
  78. if (tmp != value.objectReferenceValue)
  79. {
  80. if (value.objectReferenceValue != null)
  81. {
  82. if (CheckForSameValue(value.objectReferenceValue, index))
  83. {
  84. value.objectReferenceValue = null;
  85. key.stringValue = "";
  86. bindType.stringValue = "";
  87. }
  88. else
  89. {
  90. string name = value.objectReferenceValue.name.Replace(" ", "");
  91. if (!CheckForReName(name, index, value.objectReferenceValue))
  92. {
  93. key.stringValue = name;
  94. bindType.stringValue = GetTypeName(value.objectReferenceValue);
  95. }
  96. else
  97. {
  98. value.objectReferenceValue = null;
  99. key.stringValue = "";
  100. bindType.stringValue = "";
  101. }
  102. }
  103. }
  104. else
  105. {
  106. key.stringValue = string.Empty;
  107. bindType.stringValue = string.Empty;
  108. }
  109. }
  110. else
  111. {
  112. if (value.objectReferenceValue != null)
  113. {
  114. string name = value.objectReferenceValue.name.Replace(" ", "");
  115. key.stringValue = name;
  116. }
  117. else
  118. {
  119. key.stringValue = string.Empty;
  120. bindType.stringValue = string.Empty;
  121. }
  122. }
  123. //type
  124. if (value.objectReferenceValue != null)
  125. {
  126. var bindTypeVal = bindType.stringValue;
  127. var typeRect = valueRect;
  128. typeRect.x += valueRect.width + 10;
  129. var options = GetObjectTypes(value.objectReferenceValue);
  130. var selectIdx = 0;
  131. if (!string.IsNullOrEmpty(bindTypeVal))
  132. {
  133. selectIdx = Array.FindIndex(options, (a) => bindTypeVal.Equals(a, StringComparison.OrdinalIgnoreCase));
  134. if (selectIdx < 0)
  135. selectIdx = 0;
  136. }
  137. selectIdx = EditorGUI.Popup(typeRect, selectIdx, options);
  138. bindType.stringValue = options[selectIdx];
  139. }
  140. EditorGUILayout.EndHorizontal();
  141. };
  142. collectList.onAddCallback = list =>
  143. {
  144. values.arraySize++;
  145. var size = values.arraySize;
  146. keys.arraySize = size;
  147. bindTypes.arraySize = size;
  148. values.GetArrayElementAtIndex(size - 1).objectReferenceValue = null;
  149. keys.GetArrayElementAtIndex(size - 1).stringValue = "";
  150. bindTypes.GetArrayElementAtIndex(size - 1).stringValue = "";
  151. };
  152. collectList.onRemoveCallback = list =>
  153. {
  154. var index = list.index;
  155. if (list.index == -1)
  156. {
  157. index = values.arraySize - 1;
  158. }
  159. values.GetArrayElementAtIndex(index).objectReferenceValue = null;
  160. keys.GetArrayElementAtIndex(index).stringValue = null;
  161. bindTypes.GetArrayElementAtIndex(index).stringValue = null;
  162. values.DeleteArrayElementAtIndex(index);
  163. keys.DeleteArrayElementAtIndex(index);
  164. bindTypes.DeleteArrayElementAtIndex(index);
  165. };
  166. DrawsearchList.drawElementCallback = (Rect rect, int index, bool selected, bool focused) =>
  167. {
  168. var key = searchList[index];
  169. SerializedProperty value = null;
  170. SerializedProperty bindType = null;
  171. for (int i = 0; i < keys.arraySize; i++)
  172. {
  173. if (keys.GetArrayElementAtIndex(i).stringValue == key)
  174. {
  175. value = values.GetArrayElementAtIndex(i);
  176. bindType = bindTypes.GetArrayElementAtIndex(i);
  177. break;
  178. }
  179. }
  180. EditorGUILayout.BeginHorizontal();
  181. GUILayout.Space(10);
  182. //key
  183. var keyRect = new Rect(rect.x, rect.y + 2, (rect.width - 20) / 3, EditorGUIUtility.singleLineHeight);
  184. EditorGUI.BeginDisabledGroup(true);
  185. EditorGUI.TextField(keyRect, key);
  186. //value
  187. var valueRect = keyRect;
  188. valueRect.x += keyRect.width + 10;
  189. Type type = objectCollector.collectorType;
  190. value.objectReferenceValue = EditorGUI.ObjectField(valueRect, value.objectReferenceValue, type, true);
  191. //type
  192. if (value.objectReferenceValue != null)
  193. {
  194. var typeRect = valueRect;
  195. typeRect.x += valueRect.width + 10;
  196. EditorGUI.TextField(typeRect, bindType.stringValue);
  197. }
  198. EditorGUI.EndDisabledGroup();
  199. EditorGUILayout.EndHorizontal();
  200. };
  201. }
  202. private string[] GetObjectTypes(UnityEngine.Object obj)
  203. {
  204. var types = new List<string>();
  205. GameObject go = null;
  206. if (obj is Component component)
  207. {
  208. go = component.gameObject;
  209. }
  210. else if (obj is GameObject tmpGo)
  211. {
  212. go = tmpGo;
  213. }
  214. if (go != null)
  215. {
  216. types.Add(typeof(GameObject).Name);
  217. types.Add(typeof(Transform).Name);
  218. var components = go.GetComponents<Component>();
  219. foreach (var item in components)
  220. {
  221. types.Add(GetTypeName(item));
  222. }
  223. }
  224. else
  225. {
  226. types.Add(GetTypeName(obj));
  227. }
  228. return types.ToArray();
  229. }
  230. const string UnityNamespace = "UnityEngine";
  231. private string GetTypeName(object obj)
  232. {
  233. var type = obj.GetType();
  234. if (type.Namespace == UnityNamespace)
  235. {
  236. return type.Name;
  237. }
  238. return type.FullName;
  239. }
  240. private bool CheckForSameValue(UnityEngine.Object value, int index)
  241. {
  242. for (int i = 0; i < values.arraySize; i++)
  243. {
  244. if (index == i) continue;
  245. var compareValue = values.GetArrayElementAtIndex(i).objectReferenceValue;
  246. if (compareValue == null) continue;
  247. if (value.GetInstanceID() == compareValue.GetInstanceID())
  248. {
  249. EditorUtility.DisplayDialog("Warning",
  250. $"已经添加过该{objectCollector.collectorType.Name}啦!",
  251. "确定");
  252. return true;
  253. }
  254. }
  255. return false;
  256. }
  257. private bool CheckForReName(string name, int index, UnityEngine.Object ob)
  258. {
  259. for (int i = 0; i < values.arraySize; i++)
  260. {
  261. if (i == index) continue;
  262. var value = values.GetArrayElementAtIndex(i);
  263. if (value.objectReferenceValue == null) continue;
  264. string compareName = value.objectReferenceValue.name.Replace(" ", "");
  265. if (!string.IsNullOrEmpty(compareName) && name == compareName)
  266. {
  267. EditorUtility.DisplayDialog("Warning",
  268. $"该{objectCollector.collectorType.Name}名称{name}重名啦!请修改GameObject名称。",
  269. "确定");
  270. EditorGUIUtility.PingObject(ob);
  271. return true;
  272. }
  273. }
  274. return false;
  275. }
  276. public override float GetPropertyHeight(SerializedProperty property, GUIContent label)
  277. {
  278. return 0;
  279. }
  280. public override void OnGUI(Rect position, SerializedProperty property, GUIContent label)
  281. {
  282. keys = property.FindPropertyRelative("keys");
  283. values = property.FindPropertyRelative("values");
  284. bindTypes = property.FindPropertyRelative("bindTypes");
  285. GUILayout.BeginVertical("HelpBox");
  286. DrawInputTextField();
  287. EditorGUILayout.Space();
  288. if (collectList == null)
  289. {
  290. collectList = new ReorderableList(property.serializedObject, values, true, false, true, true);
  291. DrawsearchList = new ReorderableList(searchList, typeof(string), true, false, false, false);
  292. OnInit(property);
  293. }
  294. if (Application.isPlaying) EditorGUI.BeginDisabledGroup(true);
  295. if (string.IsNullOrEmpty(m_InputSearchText))
  296. {
  297. GUI.contentColor = Color.white;
  298. var dragArea = GUILayoutUtility.GetRect(0f, 45f);
  299. GUI.color = Color.grey;
  300. UnityEngine.Object ob = DragAreaGUI.ObjectField(dragArea, objectCollector.collectorType, objectCollector.blackTypes);
  301. if (ob != null)
  302. AddObjectToList(ob);
  303. GUI.color = Color.white;
  304. GUILayout.Space(8);
  305. collectList.DoLayoutList();
  306. }
  307. else
  308. {
  309. GetSearchList();
  310. DrawsearchList.DoLayoutList();
  311. }
  312. GUILayout.EndVertical();
  313. if (Application.isPlaying) EditorGUI.EndDisabledGroup();
  314. }
  315. private void AddObjectToList(UnityEngine.Object ob)
  316. {
  317. if (CheckForSameValue(ob, -1)) return;
  318. string name = ob.name.Replace(" ", "");
  319. if (CheckForReName(name, -1, ob)) return;
  320. values.arraySize++;
  321. var size = values.arraySize;
  322. keys.arraySize = size;
  323. bindTypes.arraySize = size;
  324. values.GetArrayElementAtIndex(size - 1).objectReferenceValue = ob;
  325. keys.GetArrayElementAtIndex(size - 1).stringValue = ob.name;
  326. bindTypes.GetArrayElementAtIndex(size - 1).stringValue = GetTypeName(ob);
  327. }
  328. private void GetSearchList()
  329. {
  330. searchList.Clear();
  331. for (int index = 0; index < keys.arraySize; index++)
  332. {
  333. var key = keys.GetArrayElementAtIndex(index);
  334. if (!string.IsNullOrEmpty(m_InputSearchText))
  335. {
  336. var inputStr = m_InputSearchText.ToLower();
  337. var compareStr = key.stringValue.ToLower();
  338. Regex r = new Regex(inputStr);
  339. Match m = r.Match(compareStr);
  340. if (m.Success)
  341. {
  342. searchList.Add(key.stringValue);
  343. }
  344. }
  345. }
  346. }
  347. private void DrawInputTextField()
  348. {
  349. if (TextFieldRoundEdge == null)
  350. {
  351. TextFieldRoundEdge = new GUIStyle("SearchTextField");
  352. TextFieldRoundEdgeCancelButton = new GUIStyle("SearchCancelButton");
  353. TextFieldRoundEdgeCancelButtonEmpty = new GUIStyle("SearchCancelButtonEmpty");
  354. TransparentTextField = new GUIStyle(EditorStyles.whiteLabel);
  355. TransparentTextField.normal.textColor = EditorStyles.textField.normal.textColor;
  356. }
  357. Rect position = EditorGUILayout.GetControlRect();
  358. GUIStyle textFieldRoundEdge = TextFieldRoundEdge;
  359. GUIStyle transparentTextField = TransparentTextField;
  360. GUIStyle gUIStyle = (m_InputSearchText != "") ? TextFieldRoundEdgeCancelButton : TextFieldRoundEdgeCancelButtonEmpty;
  361. position.width -= gUIStyle.fixedWidth;
  362. if (Event.current.type == EventType.Repaint)
  363. {
  364. GUI.contentColor = (EditorGUIUtility.isProSkin ? Color.white : Color.black);
  365. if (string.IsNullOrEmpty(m_InputSearchText))
  366. {
  367. textFieldRoundEdge.Draw(position, new GUIContent("输入查询,可模糊匹配"), 0);
  368. }
  369. else
  370. {
  371. textFieldRoundEdge.Draw(position, new GUIContent(""), 0);
  372. }
  373. GUI.contentColor = Color.white;
  374. }
  375. Rect rect = position;
  376. float num = textFieldRoundEdge.CalcSize(new GUIContent("")).x - 2f;
  377. rect.width -= num;
  378. rect.x += num;
  379. rect.y += 1f;
  380. m_InputSearchText = EditorGUI.TextField(rect, m_InputSearchText, transparentTextField);
  381. position.x += position.width;
  382. position.width = gUIStyle.fixedWidth;
  383. position.height = gUIStyle.fixedHeight;
  384. if (GUI.Button(position, GUIContent.none, gUIStyle) && m_InputSearchText != "")
  385. {
  386. m_InputSearchText = "";
  387. GUI.changed = true;
  388. GUIUtility.keyboardControl = 0;
  389. }
  390. }
  391. }
  392. }