AlicizaX/Client/Packages/com.alicizax.unity.editor.extension/Editor/Table/TableView.cs
2025-01-26 20:55:39 +08:00

448 lines
14 KiB
C#

using System;
using System.Collections.Generic;
using UnityEditor;
using UnityEditor.IMGUI.Controls;
using UnityEngine;
namespace AlicizaX.EditorExtension.Editor
{
/// <summary>
/// 表格视图
/// </summary>
/// <typeparam name="T">数据类型</typeparam>
public sealed class TableView<T> : TreeView where T : class, new()
{
/// <summary>
/// 表格数据
/// </summary>
private List<T> _datas;
/// <summary>
/// 当前选择的表格数据
/// </summary>
private List<T> _selectionDatas;
/// <summary>
/// 根元素
/// </summary>
private TableViewItem<T> _rootItem;
/// <summary>
/// 所有的元素
/// </summary>
private List<TableViewItem<T>> _items;
/// <summary>
/// 所有的元素绘制项
/// </summary>
private List<TreeViewItem> _drawItems;
/// <summary>
/// 搜索控件
/// </summary>
private SearchField _searchField;
/// <summary>
/// 元素ID标记
/// </summary>
private int _idSign = 0;
/// <summary>
/// 当选择项改变
/// </summary>
public event Action<List<T>> OnSelectionChanged;
/// <summary>
/// 搜索数据时的方法
/// </summary>
public Func<T, string, bool> OnSearch;
/// <summary>
/// 行高度
/// </summary>
public float RowHeight
{
get
{
return rowHeight;
}
set
{
rowHeight = value;
}
}
/// <summary>
/// 是否启用上下文右键点击
/// </summary>
public bool IsEnableContextClick { get; set; } = true;
/// <summary>
/// 是否允许多选
/// </summary>
public bool IsCanMultiSelect { get; set; } = true;
/// <summary>
/// 是否启用搜索框
/// </summary>
public bool IsEnableSearch { get; set; } = false;
/// <summary>
/// 表格视图
/// </summary>
/// <param name="datas">表格视图数据</param>
/// <param name="columns">表格视图的所有列</param>
public TableView(List<T> datas, List<TableColumn<T>> columns) : base(new TreeViewState())
{
showAlternatingRowBackgrounds = true;
showBorder = true;
rowHeight = EditorGUIUtility.singleLineHeight + 4;
columns.Insert(0, GetIndexColumn());
multiColumnHeader = new MultiColumnHeader(new MultiColumnHeaderState(columns.ToArray()));
multiColumnHeader.sortingChanged += OnSortingChanged;
multiColumnHeader.visibleColumnsChanged += OnVisibleColumnsChanged;
_datas = datas;
_selectionDatas = new List<T>();
_rootItem = new TableViewItem<T>(-1, -1, null);
_items = new List<TableViewItem<T>>();
for (var i = 0; i < _datas.Count; i++)
{
_items.Add(new TableViewItem<T>(_idSign, 0, _datas[i]));
_idSign += 1;
}
_drawItems = new List<TreeViewItem>();
_searchField = new SearchField();
Reload();
}
/// <summary>
/// 构造根节点
/// </summary>
protected override TreeViewItem BuildRoot()
{
return _rootItem;
}
/// <summary>
/// 构造所有行
/// </summary>
protected override IList<TreeViewItem> BuildRows(TreeViewItem root)
{
_drawItems.Clear();
if (hasSearch && OnSearch != null)
{
for (int i = 0; i < _items.Count; i++)
{
if (OnSearch(_items[i].Data, searchString))
{
_drawItems.Add(_items[i]);
}
}
}
else
{
for (int i = 0; i < _items.Count; i++)
{
_drawItems.Add(_items[i]);
}
}
return _drawItems;
}
/// <summary>
/// 绘制行
/// </summary>
protected override void RowGUI(RowGUIArgs args)
{
TableViewItem<T> item = args.item as TableViewItem<T>;
int visibleColumns = args.GetNumVisibleColumns();
for (var i = 0; i < visibleColumns; i++)
{
Rect cellRect = args.GetCellRect(i);
int index = args.GetColumn(i);
CenterRectUsingSingleLineHeight(ref cellRect);
TableColumn<T> column = multiColumnHeader.GetColumn(index) as TableColumn<T>;
column.DrawCell?.Invoke(cellRect, item.Data, args.row, args.selected, args.focused);
}
}
/// <summary>
/// 上下文右键点击
/// </summary>
protected override void ContextClicked()
{
if (!IsEnableContextClick)
return;
List<T> selectedItems = new List<T>();
foreach (var itemID in GetSelection())
{
TableViewItem<T> item = _items.Find((it) => { return it.id == itemID; });
if (item != null) selectedItems.Add(item.Data);
}
GenericMenu menu = new GenericMenu();
menu.AddItem(new GUIContent("Add row"), false, () =>
{
AddData(new T());
});
if (selectedItems.Count > 0)
{
menu.AddItem(new GUIContent("Delete selected rows"), false, () =>
{
DeleteDatas(selectedItems);
});
}
menu.AddSeparator("");
menu.AddItem(new GUIContent("Clear rows"), false, () =>
{
ClearData();
});
menu.ShowAsContext();
}
/// <summary>
/// 当选择项改变
/// </summary>
protected override void SelectionChanged(IList<int> selectedIds)
{
base.SelectionChanged(selectedIds);
_selectionDatas.Clear();
foreach (var itemID in selectedIds)
{
TableViewItem<T> item = _items.Find((it) => { return it.id == itemID; });
if (item != null) _selectionDatas.Add(item.Data);
}
OnSelectionChanged?.Invoke(_selectionDatas);
}
/// <summary>
/// 是否允许多选
/// </summary>
protected override bool CanMultiSelect(TreeViewItem item)
{
return IsCanMultiSelect;
}
/// <summary>
/// 绘制表格视图
/// </summary>
/// <param name="rect">绘制区域</param>
public override void OnGUI(Rect rect)
{
if (IsEnableSearch)
{
Rect sub = new Rect(rect.x, rect.y, 60, 16);
EditorGUI.LabelField(sub, "Search: ");
sub.Set(rect.x + 60, rect.y, rect.width - 60, 18);
searchString = _searchField.OnGUI(sub, searchString);
rect.y += 18;
rect.height -= 18;
base.OnGUI(rect);
}
else
{
base.OnGUI(rect);
}
}
/// <summary>
/// 获取索引列
/// </summary>
private TableColumn<T> GetIndexColumn()
{
TableColumn<T> column = new TableColumn<T>();
column.autoResize = false;
column.headerContent = new GUIContent("Index");
column.width = 50;
column.canSort = false;
column.Compare = null;
column.DrawCell = (rect, data, rowIndex, isSelected, isFocused) =>
{
EditorGUI.LabelField(rect, rowIndex.ToString());
};
return column;
}
/// <summary>
/// 当重新排序
/// </summary>
private void OnSortingChanged(MultiColumnHeader columnheader)
{
bool isAscending = multiColumnHeader.IsSortedAscending(multiColumnHeader.sortedColumnIndex);
TableColumn<T> column = multiColumnHeader.GetColumn(multiColumnHeader.sortedColumnIndex) as TableColumn<T>;
if (column.Compare != null)
{
_items.Sort((a, b) =>
{
if (isAscending)
{
return -column.Compare(a.Data, b.Data);
}
else
{
return column.Compare(a.Data, b.Data);
}
});
Reload();
}
}
/// <summary>
/// 当列激活状态改变
/// </summary>
private void OnVisibleColumnsChanged(MultiColumnHeader columnheader)
{
Reload();
}
/// <summary>
/// 添加数据
/// </summary>
/// <param name="data">数据</param>
public void AddData(T data)
{
if (_datas.Contains(data))
return;
_datas.Add(data);
_items.Add(new TableViewItem<T>(_idSign, 0, data));
_idSign += 1;
Reload();
}
/// <summary>
/// 添加数据
/// </summary>
/// <param name="datas">数据</param>
public void AddDatas(List<T> datas)
{
for (int i = 0; i < datas.Count; i++)
{
T data = datas[i];
if (_datas.Contains(data))
continue;
_datas.Add(data);
_items.Add(new TableViewItem<T>(_idSign, 0, data));
_idSign += 1;
}
Reload();
}
/// <summary>
/// 删除数据
/// </summary>
/// <param name="data">数据</param>
public void DeleteData(T data)
{
if (!_datas.Contains(data))
return;
_datas.Remove(data);
TableViewItem<T> item = _items.Find((i) => { return i.Data == data; });
if (item != null)
{
_items.Remove(item);
}
Reload();
}
/// <summary>
/// 删除数据
/// </summary>
/// <param name="datas">数据</param>
public void DeleteDatas(List<T> datas)
{
for (int i = 0; i < datas.Count; i++)
{
T data = datas[i];
if (!_datas.Contains(data))
continue;
_datas.Remove(data);
TableViewItem<T> item = _items.Find((t) => { return t.Data == data; });
if (item != null)
{
_items.Remove(item);
}
}
Reload();
}
/// <summary>
/// 选中数据
/// </summary>
/// <param name="data">数据</param>
public void SelectData(T data)
{
if (!_datas.Contains(data))
return;
TableViewItem<T> item = _items.Find((i) => { return i.Data == data; });
if (item != null)
{
SetSelection(new int[] { item.id });
}
}
/// <summary>
/// 选中数据
/// </summary>
/// <param name="data">数据</param>
/// <param name="options">选中的操作</param>
public void SelectData(T data, TreeViewSelectionOptions options)
{
if (!_datas.Contains(data))
return;
TableViewItem<T> item = _items.Find((i) => { return i.Data == data; });
if (item != null)
{
SetSelection(new int[] { item.id }, options);
}
}
/// <summary>
/// 选中数据
/// </summary>
/// <param name="datas">数据</param>
public void SelectDatas(List<T> datas)
{
List<int> ids = new List<int>();
for (int i = 0; i < datas.Count; i++)
{
T data = datas[i];
if (!_datas.Contains(data))
continue;
TableViewItem<T> item = _items.Find((t) => { return t.Data == data; });
if (item != null)
{
ids.Add(item.id);
}
}
if (ids.Count > 0)
{
SetSelection(ids);
}
}
/// <summary>
/// 选中数据
/// </summary>
/// <param name="datas">数据</param>
/// <param name="options">选中的操作</param>
public void SelectDatas(List<T> datas, TreeViewSelectionOptions options)
{
List<int> ids = new List<int>();
for (int i = 0; i < datas.Count; i++)
{
T data = datas[i];
if (!_datas.Contains(data))
continue;
TableViewItem<T> item = _items.Find((t) => { return t.Data == data; });
if (item != null)
{
ids.Add(item.id);
}
}
if (ids.Count > 0)
{
SetSelection(ids, options);
}
}
/// <summary>
/// 清空所有数据
/// </summary>
public void ClearData()
{
_datas.Clear();
_items.Clear();
Reload();
}
}
}