Unity 框架

TableView.cs 20KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617
  1. /****************************************************
  2. 文件:TableView.cs
  3. 作者:陶长春
  4. 邮箱:376248129@qq.com
  5. 日期:2024年12月12日 8:25:50
  6. UnityVersion: 2021.3.13f1
  7. 功能:表格绘制
  8. *****************************************************/
  9. using System.Collections;
  10. using UnityEngine;
  11. using System.Collections.Generic;
  12. using UnityEditor.IMGUI.Controls;
  13. using UnityEditor;
  14. namespace TFramework
  15. {
  16. public class TableView<T> : TreeView where T : class, new()
  17. {
  18. /// <summary>
  19. /// 表格数据
  20. /// </summary>
  21. private List<T> _datas;
  22. /// <summary>
  23. /// 当前选择的表格数据
  24. /// </summary>
  25. private List<T> _selectionDatas;
  26. /// <summary>
  27. /// 根元素
  28. /// </summary>
  29. private TableViewItem<T> _rootItem;
  30. /// <summary>
  31. /// 所有的元素
  32. /// </summary>
  33. private List<TableViewItem<T>> _items;
  34. /// <summary>
  35. /// 所有的元素绘制项
  36. /// </summary>
  37. private List<TreeViewItem> _drawItems;
  38. /// <summary>
  39. /// 搜索控件
  40. /// </summary>
  41. private SearchField _searchField;
  42. /// <summary>
  43. /// 元素ID标记
  44. /// </summary>
  45. private int _idSign = 0;
  46. /// <summary>
  47. /// 当选择项改变
  48. /// </summary>
  49. public event TAction<List<T>> OnSelectionChanged;
  50. /// <summary>
  51. /// 搜索数据时的方法
  52. /// </summary>
  53. public TFunc<T, string, bool> OnSearch;
  54. /// <summary>
  55. /// 添加右键单击上下文菜单项
  56. /// </summary>
  57. public TAction<GenericMenu> AddContextMenu;
  58. /// <summary>
  59. /// 行高度
  60. /// </summary>
  61. public float RowHeight
  62. {
  63. get
  64. {
  65. return rowHeight;
  66. }
  67. set
  68. {
  69. rowHeight = value;
  70. }
  71. }
  72. /// <summary>
  73. /// 是否启用上下文右键点击
  74. /// </summary>
  75. public bool IsEnableContextClick { get; set; } = true;
  76. /// <summary>
  77. /// 是否允许多选
  78. /// </summary>
  79. public bool IsCanMultiSelect { get; set; } = true;
  80. /// <summary>
  81. /// 是否启用搜索框
  82. /// </summary>
  83. public bool IsEnableSearch { get; set; } = false;
  84. /// <summary>
  85. /// 绘制搜索框
  86. /// </summary>
  87. /// <param>"arg0"是整个表格的矩形</param>
  88. public TFunc<Rect, Rect> DrawSearch;
  89. /// <summary>
  90. /// 是否启用筛选
  91. /// </summary>
  92. public bool IsEnableFiltrate { get; set; } = false;
  93. public List<TableViewItem<T>> Items => _items;
  94. /// <summary>
  95. /// 筛选时执行的方法
  96. /// </summary>
  97. public TFunc<T, bool> OnFiltrte;
  98. /// <summary>
  99. /// 表格视图
  100. /// </summary>
  101. /// <param name="datas">表格数据</param>
  102. /// <param name="columns">列标题数据</param>
  103. /// <param name="columnHeader">自定义列标题</param>
  104. /// <param name="isShowIndex">是否显示换序行号</param>
  105. public TableView(List<T> datas, List<TableColumn<T>> columns, TableColumnHeader<T> columnHeader = null,bool isShowIndex=true) : base(new TreeViewState())
  106. {
  107. showAlternatingRowBackgrounds = true;
  108. showBorder = true;
  109. rowHeight = EditorGUIUtility.singleLineHeight + 4;
  110. if (isShowIndex)
  111. columns.Insert(0, GetIndexColumn());
  112. if (columnHeader == null)
  113. multiColumnHeader = new TableColumnHeader<T>(new MultiColumnHeaderState(columns.ToArray()));
  114. else
  115. multiColumnHeader = columnHeader;
  116. multiColumnHeader.sortingChanged += OnSortingChanged;
  117. multiColumnHeader.visibleColumnsChanged += OnVisibleColumnsChanged;
  118. _datas = datas;
  119. _selectionDatas = new List<T>();
  120. _rootItem = new TableViewItem<T>(-1, -1, null);
  121. _items = new List<TableViewItem<T>>();
  122. for (var i = 0; i < _datas.Count; i++)
  123. {
  124. _items.Add(new TableViewItem<T>(_idSign, 0, _datas[i]));
  125. _idSign += 1;
  126. }
  127. _drawItems = new List<TreeViewItem>();
  128. _searchField = new SearchField();
  129. Reload();
  130. }
  131. /// <summary>
  132. /// 当列激活状态改变
  133. /// </summary>
  134. private void OnVisibleColumnsChanged(MultiColumnHeader multiColumnHeader)
  135. {
  136. Reload();
  137. }
  138. /// <summary>
  139. /// 当重新排序
  140. /// </summary>
  141. private void OnSortingChanged(MultiColumnHeader multiColumnHeader)
  142. {
  143. bool isAscending = multiColumnHeader.IsSortedAscending(multiColumnHeader.sortedColumnIndex);
  144. TableColumn<T> column = multiColumnHeader.GetColumn(multiColumnHeader.sortedColumnIndex) as TableColumn<T>;
  145. if (column.Compare != null)
  146. {
  147. _items.Sort((a, b) =>
  148. {
  149. if (isAscending)
  150. {
  151. return -column.Compare(a.Data, b.Data);
  152. }
  153. else
  154. {
  155. return column.Compare(a.Data, b.Data);
  156. }
  157. });
  158. Reload();
  159. }
  160. }
  161. /// <summary>
  162. /// 获取项索引
  163. /// </summary>
  164. /// <returns></returns>
  165. private TableColumn<T> GetIndexColumn()
  166. {
  167. TableColumn<T> column = new TableColumn<T>();
  168. column.autoResize = false;//自适应列大小
  169. column.headerContent = new GUIContent("Index");
  170. column.width = 50;
  171. column.canSort = false;//不对列启用排序
  172. column.Compare = null;
  173. column.DrawCell = (rect, data, rowIndex, isSelected, isFocused) =>
  174. {
  175. if (GUI.Button(new Rect(rect.x, rect.y, 14, rect.height), new GUIContent() { text = "▲", tooltip = "上移" }, EditorStyles.miniButtonRight))
  176. {
  177. MoveDataIndex(data, 1);
  178. }
  179. if (GUI.Button(new Rect(rect.x + 15, rect.y, 14, rect.height), new GUIContent() { text = "▼", tooltip = "下移" }, EditorStyles.miniButtonRight))
  180. {
  181. MoveDataIndex(data, 0);
  182. }
  183. EditorGUI.LabelField(new Rect(rect.x + 30, rect.y, rect.width - 30, rect.height), rowIndex.ToString());
  184. };
  185. return column;
  186. }
  187. /// <summary>
  188. /// 构造根节点
  189. /// </summary>
  190. protected override TreeViewItem BuildRoot()
  191. {
  192. return _rootItem;
  193. }
  194. /// <summary>
  195. /// 构造所有行
  196. /// </summary>
  197. protected override IList<TreeViewItem> BuildRows(TreeViewItem root)
  198. {
  199. _drawItems.Clear();
  200. if (hasSearch && OnSearch != null)
  201. {
  202. for (int i = 0; i < _items.Count; i++)
  203. {
  204. if (OnSearch(_items[i].Data, searchString))
  205. {
  206. if (_items[i].parent != null && _items[i].parent.id != -1 && !IsExpanded(_items[i].parent.id)) continue;
  207. _drawItems.Add(_items[i]);
  208. }
  209. }
  210. }
  211. else if (IsEnableFiltrate && OnFiltrte != null)
  212. {
  213. for (int i = 0; i < _items.Count; i++)
  214. {
  215. if (OnFiltrte(_items[i].Data))
  216. {
  217. if (_items[i].parent != null && _items[i].parent.id != -1 && !IsExpanded(_items[i].parent.id)) continue;
  218. _drawItems.Add(_items[i]);
  219. }
  220. }
  221. }
  222. else
  223. {
  224. for (int i = 0; i < _items.Count; i++)
  225. {
  226. if (_items[i].parent != null && _items[i].parent.id != -1 && !IsExpanded(_items[i].parent.id)) continue;
  227. _drawItems.Add(_items[i]);
  228. }
  229. }
  230. SetupDepthsFromParentsAndChildren(root);
  231. return _drawItems;
  232. }
  233. /// <summary>
  234. /// 绘制行
  235. /// </summary>
  236. protected override void RowGUI(RowGUIArgs args)
  237. {
  238. TableViewItem<T> item = args.item as TableViewItem<T>;
  239. if (item.parent!=null && !IsExpanded(item.parent.id)&&item.parent.id!=-1) return;
  240. int visibleColumns = args.GetNumVisibleColumns();
  241. for (var i = 0; i < visibleColumns; i++)
  242. {
  243. Rect cellRect = args.GetCellRect(i);
  244. int index = args.GetColumn(i);
  245. CenterRectUsingSingleLineHeight(ref cellRect);
  246. TableColumn<T> column = multiColumnHeader.GetColumn(index) as TableColumn<T>;
  247. if (item.hasChildren&&i==0)
  248. {
  249. cellRect.x += 13;
  250. cellRect.width -= 13;
  251. }
  252. if (item.parent != null&&item.parent.id!=-1&&i==0)
  253. {
  254. cellRect.x += 13 + 3 * item.depth+13* item.parent.depth;
  255. cellRect.width -= 13 + 3 * item.depth + 13 * item.parent.depth;
  256. }
  257. column.DrawCell?.Invoke(cellRect, item.Data, args.row, args.selected, args.focused);
  258. }
  259. }
  260. /// <summary>
  261. /// 上下文右键点击
  262. /// </summary>
  263. protected override void ContextClicked()
  264. {
  265. if (!IsEnableContextClick)
  266. return;
  267. List<TableViewItem<T>> selectedItems = new List<TableViewItem<T>>();
  268. foreach (var itemID in GetSelection())
  269. {
  270. TableViewItem<T> item = _items.Find((it) => { return it.id == itemID; });
  271. if (item != null) selectedItems.Add(item);
  272. }
  273. GenericMenu menu = new GenericMenu();
  274. menu.AddItem(new GUIContent("增加一行"), false, () =>
  275. {
  276. AddData(new T());
  277. });
  278. if (selectedItems.Count == 1)
  279. {
  280. menu.AddItem(new GUIContent("添加子行"), false, () =>
  281. {
  282. AddChliderData(selectedItems[0]);
  283. });
  284. }
  285. if (selectedItems.Count > 0)
  286. {
  287. menu.AddItem(new GUIContent("删除选中行"), false, () =>
  288. {
  289. DeleteDatas(selectedItems);
  290. });
  291. }
  292. menu.AddSeparator("");
  293. menu.AddItem(new GUIContent("删除所有行"), false, () =>
  294. {
  295. ClearData();
  296. });
  297. AddContextMenu?.Invoke(menu);
  298. menu.ShowAsContext();
  299. }
  300. /// <summary>
  301. /// 当选择项改变
  302. /// </summary>
  303. protected override void SelectionChanged(IList<int> selectedIds)
  304. {
  305. base.SelectionChanged(selectedIds);
  306. _selectionDatas.Clear();
  307. foreach (var itemID in selectedIds)
  308. {
  309. TableViewItem<T> item = _items.Find((it) => { return it.id == itemID; });
  310. if (item != null) _selectionDatas.Add(item.Data);
  311. }
  312. OnSelectionChanged?.Invoke(_selectionDatas);
  313. }
  314. /// <summary>
  315. /// 是否允许多选
  316. /// </summary>
  317. protected override bool CanMultiSelect(TreeViewItem item)
  318. {
  319. return IsCanMultiSelect;
  320. }
  321. /// <summary>
  322. /// 绘制表格视图
  323. /// </summary>
  324. /// <param name="rect">绘制区域</param>
  325. public override void OnGUI(Rect rect)
  326. {
  327. if (IsEnableSearch)
  328. {
  329. Rect sub = new Rect(rect.x, rect.y, 60, 16);
  330. if (DrawSearch != null)
  331. {
  332. sub = DrawSearch.Invoke(rect);
  333. }
  334. else
  335. {
  336. EditorGUI.LabelField(sub, "Search: ");
  337. sub.Set(rect.x + 60, rect.y, rect.width - 60, 18);
  338. }
  339. searchString = _searchField.OnGUI(sub, searchString);
  340. rect.y += sub.height;
  341. rect.height -= sub.height;
  342. base.OnGUI(rect);
  343. }
  344. else
  345. {
  346. base.OnGUI(rect);
  347. }
  348. }
  349. /// <summary>
  350. /// 添加数据
  351. /// </summary>
  352. /// <param name="data">数据</param>
  353. public void AddData(T data)
  354. {
  355. if (_datas.Contains(data))
  356. return;
  357. _datas.Add(data);
  358. _items.Add(new TableViewItem<T>(_idSign, 0, data));
  359. _idSign += 1;
  360. Reload();
  361. }
  362. /// <summary>
  363. /// 添加子数据
  364. /// </summary>
  365. /// <param name="selectedItems"></param>
  366. private void AddChliderData(TableViewItem<T> selectedItem)
  367. {
  368. T data = new T();
  369. _datas.Add(data);
  370. TableViewItem<T> item = new TableViewItem<T>(_idSign, selectedItem.depth+1, data);
  371. item.parent = selectedItem;
  372. item.parent.id = selectedItem.id;
  373. selectedItem.AddChild(item);
  374. _items.Add(item);
  375. _idSign += 1;
  376. Reload();
  377. }
  378. /// <summary>
  379. /// 添加数据
  380. /// </summary>
  381. /// <param name="datas">数据</param>
  382. public void AddDatas(List<T> datas)
  383. {
  384. for (int i = 0; i < datas.Count; i++)
  385. {
  386. T data = datas[i];
  387. if (_datas.Contains(data))
  388. continue;
  389. _datas.Add(data);
  390. _items.Add(new TableViewItem<T>(_idSign, 0, data));
  391. _idSign += 1;
  392. }
  393. Reload();
  394. }
  395. /// <summary>
  396. /// 删除数据
  397. /// </summary>
  398. /// <param name="data">数据</param>
  399. public void DeleteData(T data)
  400. {
  401. if (!_datas.Contains(data))
  402. return;
  403. _datas.Remove(data);
  404. TableViewItem<T> item = _items.Find((i) => { return i.Data == data; });
  405. if (item != null)
  406. {
  407. if (item.parent != null)
  408. {
  409. item.parent.children.Remove(item);
  410. item.parent = null;
  411. }
  412. if (item.hasChildren)
  413. {
  414. for (int j = 0; j < item.children.Count; j++)
  415. {
  416. TableViewItem<T> viewItem = item.children[j] as TableViewItem<T>;
  417. DeleteData(viewItem.Data);
  418. }
  419. }
  420. _items.Remove(item);
  421. }
  422. Reload();
  423. }
  424. /// <summary>
  425. /// 删除数据
  426. /// </summary>
  427. /// <param name="datas">数据</param>
  428. public void DeleteDatas(List<TableViewItem<T>> datas)
  429. {
  430. for (int i = 0; i < datas.Count; i++)
  431. {
  432. T data = datas[i].Data;
  433. if (!_datas.Contains(data))
  434. continue;
  435. DeleteData(data);
  436. //_datas.Remove(data);
  437. //TableViewItem<T> item = _items.Find((t) => { return t.Data == data; });
  438. //if (item != null)
  439. //{
  440. // if (item.parent != null)
  441. // {
  442. // item.parent.children.Remove(item);
  443. // item.parent = null;
  444. // }
  445. // if (item.hasChildren)
  446. // {
  447. // for (int j = 0; j < item.children.Count; j++)
  448. // {
  449. // TableViewItem<T> viewItem = item.children[j] as TableViewItem<T>;
  450. // DeleteData(viewItem.Data);
  451. // }
  452. // }
  453. // _items.Remove(item);
  454. //}
  455. }
  456. //Reload();
  457. }
  458. /// <summary>
  459. /// 选中数据
  460. /// </summary>
  461. /// <param name="data">数据</param>
  462. public void SelectData(T data)
  463. {
  464. if (!_datas.Contains(data))
  465. return;
  466. TableViewItem<T> item = _items.Find((i) => { return i.Data == data; });
  467. if (item != null)
  468. {
  469. SetSelection(new int[] { item.id });
  470. }
  471. }
  472. /// <summary>
  473. /// 选中数据
  474. /// </summary>
  475. /// <param name="data">数据</param>
  476. /// <param name="options">选中的操作</param>
  477. public void SelectData(T data, TreeViewSelectionOptions options)
  478. {
  479. if (!_datas.Contains(data))
  480. return;
  481. TableViewItem<T> item = _items.Find((i) => { return i.Data == data; });
  482. if (item != null)
  483. {
  484. SetSelection(new int[] { item.id }, options);
  485. }
  486. }
  487. /// <summary>
  488. /// 选中数据
  489. /// </summary>
  490. /// <param name="datas">数据</param>
  491. public void SelectDatas(List<T> datas)
  492. {
  493. List<int> ids = new List<int>();
  494. for (int i = 0; i < datas.Count; i++)
  495. {
  496. T data = datas[i];
  497. if (!_datas.Contains(data))
  498. continue;
  499. TableViewItem<T> item = _items.Find((t) => { return t.Data == data; });
  500. if (item != null)
  501. {
  502. ids.Add(item.id);
  503. }
  504. }
  505. if (ids.Count > 0)
  506. {
  507. SetSelection(ids);
  508. }
  509. }
  510. /// <summary>
  511. /// 选中数据
  512. /// </summary>
  513. /// <param name="datas">数据</param>
  514. /// <param name="options">选中的操作</param>
  515. public void SelectDatas(List<T> datas, TreeViewSelectionOptions options)
  516. {
  517. List<int> ids = new List<int>();
  518. for (int i = 0; i < datas.Count; i++)
  519. {
  520. T data = datas[i];
  521. if (!_datas.Contains(data))
  522. continue;
  523. TableViewItem<T> item = _items.Find((t) => { return t.Data == data; });
  524. if (item != null)
  525. {
  526. ids.Add(item.id);
  527. }
  528. }
  529. if (ids.Count > 0)
  530. {
  531. SetSelection(ids, options);
  532. }
  533. }
  534. /// <summary>
  535. /// 清空所有数据
  536. /// </summary>
  537. public void ClearData()
  538. {
  539. _datas.Clear();
  540. _items.Clear();
  541. Reload();
  542. }
  543. /// <summary>
  544. /// 移动数据位置
  545. /// </summary>
  546. /// <param name="data">数据</param>
  547. /// <param name="AorS">上移:1,下移:0</param>
  548. public void MoveDataIndex(T data, int AorS)
  549. {
  550. if (!_datas.Contains(data))
  551. return;
  552. TableViewItem<T> item = _items.Find((i) => { return i.Data == data; });
  553. if (AorS == 0)
  554. {
  555. if (_datas.IndexOf(data) + 1 >= _datas.Count) return;
  556. _datas.Swap(_datas.IndexOf(data), _datas.IndexOf(data) + 1);
  557. if (item != null && _items.IndexOf(item) + 1 < _items.Count)
  558. {
  559. _items.Swap(_items.IndexOf(item), _items.IndexOf(item) + 1);
  560. }
  561. }
  562. else
  563. {
  564. if (_datas.IndexOf(data) - 1 < 0) return;
  565. _datas.Swap(_datas.IndexOf(data), _datas.IndexOf(data) - 1);
  566. if (item != null && _items.IndexOf(item) - 1 >= 0)
  567. {
  568. _items.Swap(_items.IndexOf(item), _items.IndexOf(item) - 1);
  569. }
  570. }
  571. Reload();
  572. }
  573. }
  574. }