383 lines
8.5 KiB
C#
383 lines
8.5 KiB
C#
using System;
|
|
using System.Collections.Generic;
|
|
using System.Linq;
|
|
using EasyLayout;
|
|
using UnityEngine;
|
|
using UnityEngine.Events;
|
|
using UnityEngine.UI;
|
|
|
|
namespace UIWidgets
|
|
{
|
|
public class TreeViewCustom<TComponent, TItem> : ListViewCustom<TComponent, ListNode<TItem>> where TComponent : ListViewItem
|
|
{
|
|
[Serializable]
|
|
public class NodeEvent : UnityEvent<TreeNode<TItem>>
|
|
{
|
|
}
|
|
|
|
[SerializeField]
|
|
private IObservableList<TreeNode<TItem>> nodes = new ObservableList<TreeNode<TItem>>();
|
|
|
|
public NodeEvent NodeToggle = new NodeEvent();
|
|
|
|
public NodeEvent NodeSelected = new NodeEvent();
|
|
|
|
public NodeEvent NodeDeselected = new NodeEvent();
|
|
|
|
protected ObservableList<ListNode<TItem>> NodesList = new ObservableList<ListNode<TItem>>();
|
|
|
|
[NonSerialized]
|
|
private bool isStartedTreeViewCustom;
|
|
|
|
private TreeNode<TItem> rootNode;
|
|
|
|
public IObservableList<TreeNode<TItem>> Nodes
|
|
{
|
|
get
|
|
{
|
|
return nodes;
|
|
}
|
|
set
|
|
{
|
|
if (nodes != null)
|
|
{
|
|
nodes.OnChange -= NodesChanged;
|
|
nodes.OnChange -= CollectionChanged;
|
|
}
|
|
nodes = value;
|
|
RootNode.Nodes = value;
|
|
SetScrollValue(0f);
|
|
Refresh();
|
|
if (nodes != null)
|
|
{
|
|
nodes.OnChange += NodesChanged;
|
|
nodes.OnChange += CollectionChanged;
|
|
CollectionChanged();
|
|
}
|
|
}
|
|
}
|
|
|
|
public TreeNode<TItem> SelectedNode
|
|
{
|
|
get
|
|
{
|
|
int count = SelectedNodes.Count;
|
|
if (count > 0)
|
|
{
|
|
return SelectedNodes[count - 1];
|
|
}
|
|
return null;
|
|
}
|
|
}
|
|
|
|
public List<TreeNode<TItem>> SelectedNodes
|
|
{
|
|
get
|
|
{
|
|
if (base.SelectedIndex == -1)
|
|
{
|
|
return new List<TreeNode<TItem>>();
|
|
}
|
|
return base.SelectedIndicies.Convert((int x) => NodesList[x].Node);
|
|
}
|
|
set
|
|
{
|
|
base.SelectedIndicies = Nodes2Indicies(value);
|
|
}
|
|
}
|
|
|
|
protected TreeNode<TItem> RootNode
|
|
{
|
|
get
|
|
{
|
|
return rootNode;
|
|
}
|
|
}
|
|
|
|
protected override void Awake()
|
|
{
|
|
Start();
|
|
}
|
|
|
|
public override void Start()
|
|
{
|
|
if (!isStartedTreeViewCustom)
|
|
{
|
|
isStartedTreeViewCustom = true;
|
|
rootNode = new TreeNode<TItem>(default(TItem));
|
|
setContentSizeFitter = false;
|
|
base.Start();
|
|
Refresh();
|
|
OnSelect.AddListener(OnSelectNode);
|
|
OnDeselect.AddListener(OnDeselectNode);
|
|
KeepSelection = true;
|
|
base.DataSource = NodesList;
|
|
}
|
|
}
|
|
|
|
private void CollectionChanged()
|
|
{
|
|
if (nodes != null)
|
|
{
|
|
}
|
|
}
|
|
|
|
private void SetParent(TreeNode<TItem> node)
|
|
{
|
|
if (node.Parent != RootNode)
|
|
{
|
|
node.Parent = RootNode;
|
|
}
|
|
}
|
|
|
|
protected override bool CanOptimize()
|
|
{
|
|
bool flag = scrollRect != null;
|
|
LayoutGroup layoutGroup = ((!(Container != null)) ? null : (layout ?? Container.GetComponent<LayoutGroup>()));
|
|
bool flag2 = layoutGroup is global::EasyLayout.EasyLayout;
|
|
return flag && flag2;
|
|
}
|
|
|
|
protected virtual int Nodes2List(IObservableList<TreeNode<TItem>> sourceNodes, int depth, ObservableList<ListNode<TItem>> list)
|
|
{
|
|
int num = 0;
|
|
foreach (TreeNode<TItem> sourceNode in sourceNodes)
|
|
{
|
|
if (sourceNode.IsVisible)
|
|
{
|
|
list.Add(new ListNode<TItem>(sourceNode, depth));
|
|
if (sourceNode.IsExpanded && sourceNode.Nodes != null && sourceNode.Nodes.Count > 0)
|
|
{
|
|
int usedNodesCount = Nodes2List(sourceNode.Nodes, depth + 1, list);
|
|
sourceNode.UsedNodesCount = usedNodesCount;
|
|
}
|
|
else
|
|
{
|
|
sourceNode.UsedNodesCount = 0;
|
|
}
|
|
num++;
|
|
}
|
|
}
|
|
return num;
|
|
}
|
|
|
|
protected void OnToggleNode(int index)
|
|
{
|
|
ToggleNode(index);
|
|
NodeToggle.Invoke(NodesList[index].Node);
|
|
}
|
|
|
|
protected void OnSelectNode(int index, ListViewItem component)
|
|
{
|
|
NodeSelected.Invoke(NodesList[index].Node);
|
|
}
|
|
|
|
protected void OnDeselectNode(int index, ListViewItem component)
|
|
{
|
|
NodeDeselected.Invoke(NodesList[index].Node);
|
|
}
|
|
|
|
protected void ToggleNode(int index)
|
|
{
|
|
ListNode<TItem> listNode = NodesList[index];
|
|
if (listNode.Node.Nodes == null)
|
|
{
|
|
return;
|
|
}
|
|
NodesList.BeginUpdate();
|
|
if (listNode.Node.IsExpanded)
|
|
{
|
|
int allUsedNodesCount = listNode.Node.AllUsedNodesCount;
|
|
if (allUsedNodesCount > 0)
|
|
{
|
|
MoveSelectedIndiciesUp(index, allUsedNodesCount);
|
|
NodesList.RemoveRange(index + 1, allUsedNodesCount);
|
|
}
|
|
listNode.Node.PauseObservation = true;
|
|
listNode.Node.IsExpanded = false;
|
|
listNode.Node.PauseObservation = false;
|
|
listNode.Node.UsedNodesCount = 0;
|
|
}
|
|
else
|
|
{
|
|
ObservableList<ListNode<TItem>> observableList = new ObservableList<ListNode<TItem>>();
|
|
int usedNodesCount = Nodes2List(listNode.Node.Nodes, listNode.Depth + 1, observableList);
|
|
MoveSelectedIndiciesDown(index, observableList.Count);
|
|
NodesList.InsertRange(index + 1, observableList);
|
|
listNode.Node.PauseObservation = true;
|
|
listNode.Node.IsExpanded = true;
|
|
listNode.Node.PauseObservation = false;
|
|
listNode.Node.UsedNodesCount = usedNodesCount;
|
|
}
|
|
NodesList.EndUpdate();
|
|
}
|
|
|
|
private void MoveSelectedIndiciesUp(int index, int range)
|
|
{
|
|
int start = index + 1;
|
|
int end = start + range;
|
|
base.SelectedIndicies.Where((int x) => start <= x && x <= end).ForEach(base.Deselect);
|
|
List<int> list = base.SelectedIndicies.Where((int x) => x > end).ToList();
|
|
List<int> indicies = list.Select((int x) => x - range).ToList();
|
|
SilentDeselect(list);
|
|
SilentSelect(indicies);
|
|
}
|
|
|
|
private void MoveSelectedIndiciesDown(int index, int range)
|
|
{
|
|
int start = index + 1;
|
|
int end = start + range;
|
|
List<int> list = base.SelectedIndicies.Where((int x) => start <= x && x <= end).ToList();
|
|
List<int> indicies = list.Convert((int x) => x + range);
|
|
SilentDeselect(list);
|
|
SilentSelect(indicies);
|
|
}
|
|
|
|
protected List<int> Nodes2Indicies(IEnumerable<TreeNode<TItem>> targetNodes)
|
|
{
|
|
return (from x in targetNodes
|
|
select NodesList.FindIndex((ListNode<TItem> y) => x == y.Node) into i
|
|
where i != -1
|
|
select i).ToList();
|
|
}
|
|
|
|
protected virtual void NodesChanged()
|
|
{
|
|
Refresh();
|
|
}
|
|
|
|
public virtual void Refresh()
|
|
{
|
|
if (nodes == null)
|
|
{
|
|
NodesList.Clear();
|
|
return;
|
|
}
|
|
NodesList.BeginUpdate();
|
|
List<TreeNode<TItem>> selectedNodes = SelectedNodes;
|
|
NodesList.Clear();
|
|
Nodes2List(nodes, 0, NodesList);
|
|
if (selectedNodes != null)
|
|
{
|
|
SilentDeselect(base.SelectedIndicies);
|
|
List<int> indicies = Nodes2Indicies(selectedNodes);
|
|
SilentSelect(indicies);
|
|
}
|
|
NodesList.EndUpdate();
|
|
}
|
|
|
|
public override void Clear()
|
|
{
|
|
nodes.Clear();
|
|
SetScrollValue(0f);
|
|
}
|
|
|
|
[Obsolete("Not supported for TreeView", true)]
|
|
public int Add(TItem item)
|
|
{
|
|
return -1;
|
|
}
|
|
|
|
[Obsolete("Not supported for TreeView", true)]
|
|
public int Remove(TItem item)
|
|
{
|
|
return -1;
|
|
}
|
|
|
|
public override void Remove(int index)
|
|
{
|
|
throw new NotSupportedException("Not supported for TreeView.");
|
|
}
|
|
|
|
[Obsolete("Not supported for TreeView", true)]
|
|
public int Set(TItem item, bool allowDuplicate = true)
|
|
{
|
|
return -1;
|
|
}
|
|
|
|
public bool Remove(Predicate<TreeNode<TItem>> match)
|
|
{
|
|
int num = FindIndex(nodes, match);
|
|
if (num != -1)
|
|
{
|
|
nodes.RemoveAt(num);
|
|
return true;
|
|
}
|
|
foreach (TreeNode<TItem> node in nodes)
|
|
{
|
|
if (node.Nodes == null || !Remove(node.Nodes, match))
|
|
{
|
|
continue;
|
|
}
|
|
return true;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
private bool Remove(IObservableList<TreeNode<TItem>> nodes, Predicate<TreeNode<TItem>> match)
|
|
{
|
|
int num = FindIndex(nodes, match);
|
|
if (num != -1)
|
|
{
|
|
nodes.RemoveAt(num);
|
|
return true;
|
|
}
|
|
foreach (TreeNode<TItem> node in nodes)
|
|
{
|
|
if (node.Nodes == null || !Remove(node.Nodes, match))
|
|
{
|
|
continue;
|
|
}
|
|
return true;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
private int FindIndex(IObservableList<TreeNode<TItem>> nodes, Predicate<TreeNode<TItem>> match)
|
|
{
|
|
if (nodes is ObservableList<TreeNode<TItem>>)
|
|
{
|
|
return (nodes as ObservableList<TreeNode<TItem>>).FindIndex(match);
|
|
}
|
|
for (int i = 0; i < nodes.Count; i++)
|
|
{
|
|
if (match(nodes[i]))
|
|
{
|
|
return i;
|
|
}
|
|
}
|
|
return -1;
|
|
}
|
|
|
|
protected override void SetData(TComponent component, ListNode<TItem> item)
|
|
{
|
|
}
|
|
|
|
protected override void RemoveCallback(ListViewItem item, int index)
|
|
{
|
|
if (item != null)
|
|
{
|
|
(item as TreeViewComponentBase<TItem>).ToggleEvent.RemoveListener(OnToggleNode);
|
|
}
|
|
base.RemoveCallback(item, index);
|
|
}
|
|
|
|
protected override void AddCallback(ListViewItem item, int index)
|
|
{
|
|
base.AddCallback(item, index);
|
|
(item as TreeViewComponentBase<TItem>).ToggleEvent.AddListener(OnToggleNode);
|
|
}
|
|
|
|
protected override void OnDestroy()
|
|
{
|
|
OnSelect.RemoveListener(OnSelectNode);
|
|
OnDeselect.RemoveListener(OnDeselectNode);
|
|
if (Nodes != null)
|
|
{
|
|
Nodes.Dispose();
|
|
}
|
|
base.OnDestroy();
|
|
}
|
|
}
|
|
}
|