364 lines
6.5 KiB
C#
364 lines
6.5 KiB
C#
using System;
|
|
using System.ComponentModel;
|
|
using System.Linq;
|
|
using UnityEngine;
|
|
|
|
namespace UIWidgets
|
|
{
|
|
[Serializable]
|
|
public class TreeNode<TItem> : IObservable, IDisposable, INotifyPropertyChanged
|
|
{
|
|
public bool PauseObservation;
|
|
|
|
[SerializeField]
|
|
private bool isVisible = true;
|
|
|
|
[SerializeField]
|
|
private bool isExpanded;
|
|
|
|
[SerializeField]
|
|
private TItem item;
|
|
|
|
[SerializeField]
|
|
private IObservableList<TreeNode<TItem>> nodes;
|
|
|
|
public int UsedNodesCount;
|
|
|
|
private WeakReference parent;
|
|
|
|
private bool disposed;
|
|
|
|
public bool IsVisible
|
|
{
|
|
get
|
|
{
|
|
return isVisible;
|
|
}
|
|
set
|
|
{
|
|
isVisible = value;
|
|
Changed("IsVisible");
|
|
}
|
|
}
|
|
|
|
public bool IsExpanded
|
|
{
|
|
get
|
|
{
|
|
return isExpanded;
|
|
}
|
|
set
|
|
{
|
|
isExpanded = value;
|
|
Changed("IsExpanded");
|
|
}
|
|
}
|
|
|
|
public TItem Item
|
|
{
|
|
get
|
|
{
|
|
return item;
|
|
}
|
|
set
|
|
{
|
|
item = value;
|
|
Changed("Item");
|
|
}
|
|
}
|
|
|
|
public IObservableList<TreeNode<TItem>> Nodes
|
|
{
|
|
get
|
|
{
|
|
return nodes;
|
|
}
|
|
set
|
|
{
|
|
if (nodes != null)
|
|
{
|
|
nodes.OnChange -= Changed;
|
|
nodes.OnCollectionChange -= CollectionChanged;
|
|
}
|
|
nodes = value;
|
|
if (nodes != null)
|
|
{
|
|
nodes.OnChange += Changed;
|
|
nodes.OnCollectionChange += CollectionChanged;
|
|
CollectionChanged();
|
|
}
|
|
Changed("Nodes");
|
|
}
|
|
}
|
|
|
|
public int TotalNodesCount
|
|
{
|
|
get
|
|
{
|
|
if (nodes == null)
|
|
{
|
|
return 1;
|
|
}
|
|
return nodes.Sum((TreeNode<TItem> x) => x.TotalNodesCount) + 1;
|
|
}
|
|
}
|
|
|
|
public int AllUsedNodesCount
|
|
{
|
|
get
|
|
{
|
|
if (!isVisible)
|
|
{
|
|
return 0;
|
|
}
|
|
if (!isExpanded)
|
|
{
|
|
return UsedNodesCount;
|
|
}
|
|
if (nodes == null)
|
|
{
|
|
return UsedNodesCount;
|
|
}
|
|
return nodes.Sum((TreeNode<TItem> x) => x.AllUsedNodesCount) + UsedNodesCount;
|
|
}
|
|
}
|
|
|
|
public TreeNode<TItem> Parent
|
|
{
|
|
get
|
|
{
|
|
if (parent != null && parent.IsAlive)
|
|
{
|
|
return parent.Target as TreeNode<TItem>;
|
|
}
|
|
return null;
|
|
}
|
|
set
|
|
{
|
|
SetParentValue(value);
|
|
}
|
|
}
|
|
|
|
public event OnChange OnChange;
|
|
|
|
public event PropertyChangedEventHandler PropertyChanged;
|
|
|
|
public TreeNode(TItem nodeItem, IObservableList<TreeNode<TItem>> nodeNodes = null, bool nodeIsExpanded = false, bool nodeIsVisible = true)
|
|
{
|
|
item = nodeItem;
|
|
nodes = nodeNodes;
|
|
isExpanded = nodeIsExpanded;
|
|
isVisible = nodeIsVisible;
|
|
if (nodes != null)
|
|
{
|
|
nodes.OnChange += Changed;
|
|
nodes.OnCollectionChange += CollectionChanged;
|
|
CollectionChanged();
|
|
}
|
|
}
|
|
|
|
public bool IsParentOfNode(TreeNode<TItem> node)
|
|
{
|
|
TreeNode<TItem> treeNode = node.Parent;
|
|
while (treeNode != null)
|
|
{
|
|
if (treeNode == this)
|
|
{
|
|
return true;
|
|
}
|
|
treeNode = treeNode.Parent;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
public bool CanBeParent(TreeNode<TItem> newParent)
|
|
{
|
|
if (this == newParent)
|
|
{
|
|
return false;
|
|
}
|
|
return !IsParentOfNode(newParent);
|
|
}
|
|
|
|
private void SetParentValue(TreeNode<TItem> newParent)
|
|
{
|
|
TreeNode<TItem> treeNode = ((parent == null || !parent.IsAlive) ? null : (parent.Target as TreeNode<TItem>));
|
|
if (treeNode == newParent)
|
|
{
|
|
return;
|
|
}
|
|
if (newParent != null)
|
|
{
|
|
if (newParent == this)
|
|
{
|
|
throw new ArgumentException("Node cannot be own parent.");
|
|
}
|
|
if (IsParentOfNode(newParent))
|
|
{
|
|
throw new ArgumentException("Own child node cannot be parent node.");
|
|
}
|
|
}
|
|
if (treeNode != null)
|
|
{
|
|
treeNode.nodes.OnCollectionChange -= treeNode.CollectionChanged;
|
|
treeNode.nodes.Remove(this);
|
|
treeNode.nodes.OnCollectionChange += treeNode.CollectionChanged;
|
|
}
|
|
parent = new WeakReference(newParent);
|
|
if (newParent != null)
|
|
{
|
|
if (newParent.nodes == null)
|
|
{
|
|
newParent.nodes = new ObservableList<TreeNode<TItem>>();
|
|
newParent.nodes.OnChange += newParent.Changed;
|
|
newParent.nodes.OnCollectionChange += newParent.CollectionChanged;
|
|
}
|
|
newParent.nodes.OnCollectionChange -= newParent.CollectionChanged;
|
|
newParent.nodes.Add(this);
|
|
newParent.nodes.OnCollectionChange += newParent.CollectionChanged;
|
|
}
|
|
}
|
|
|
|
private void CollectionChanged()
|
|
{
|
|
if (nodes != null)
|
|
{
|
|
nodes.ForEach(SetParent);
|
|
}
|
|
}
|
|
|
|
private void SetParent(TreeNode<TItem> node)
|
|
{
|
|
if (node.Parent != null && node.Parent != this)
|
|
{
|
|
node.Parent.nodes.Remove(node);
|
|
}
|
|
node.parent = new WeakReference(this);
|
|
}
|
|
|
|
private void Changed()
|
|
{
|
|
Changed("Nodes");
|
|
}
|
|
|
|
private void Changed(string propertyName)
|
|
{
|
|
if (!PauseObservation)
|
|
{
|
|
if (this.OnChange != null)
|
|
{
|
|
this.OnChange();
|
|
}
|
|
if (this.PropertyChanged != null)
|
|
{
|
|
this.PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
|
|
}
|
|
}
|
|
}
|
|
|
|
public override bool Equals(object obj)
|
|
{
|
|
TreeNode<TItem> treeNode = obj as TreeNode<TItem>;
|
|
if (treeNode == null)
|
|
{
|
|
return this == null;
|
|
}
|
|
if (this == null)
|
|
{
|
|
return false;
|
|
}
|
|
return item.Equals(treeNode.item);
|
|
}
|
|
|
|
public override int GetHashCode()
|
|
{
|
|
return base.GetHashCode();
|
|
}
|
|
|
|
public static bool operator ==(TreeNode<TItem> a, TreeNode<TItem> b)
|
|
{
|
|
bool flag = object.ReferenceEquals(null, a);
|
|
bool flag2 = object.ReferenceEquals(null, b);
|
|
if (flag && flag2)
|
|
{
|
|
return true;
|
|
}
|
|
if (flag != flag2)
|
|
{
|
|
return false;
|
|
}
|
|
bool flag3 = object.ReferenceEquals(null, a.item);
|
|
bool flag4 = object.ReferenceEquals(null, b.item);
|
|
if (flag3 && flag4)
|
|
{
|
|
return true;
|
|
}
|
|
if (flag3 != flag4)
|
|
{
|
|
return false;
|
|
}
|
|
return a.item.Equals(b.item);
|
|
}
|
|
|
|
public static bool operator !=(TreeNode<TItem> a, TreeNode<TItem> b)
|
|
{
|
|
bool flag = object.ReferenceEquals(null, a);
|
|
bool flag2 = object.ReferenceEquals(null, b);
|
|
if (flag && flag2)
|
|
{
|
|
return false;
|
|
}
|
|
if (flag != flag2)
|
|
{
|
|
return true;
|
|
}
|
|
bool flag3 = object.ReferenceEquals(null, a.item);
|
|
bool flag4 = object.ReferenceEquals(null, b.item);
|
|
if (flag3 && flag4)
|
|
{
|
|
return false;
|
|
}
|
|
if (flag3 != flag4)
|
|
{
|
|
return true;
|
|
}
|
|
return !a.item.Equals(b.item);
|
|
}
|
|
|
|
public void Dispose()
|
|
{
|
|
Dispose(true);
|
|
GC.SuppressFinalize(this);
|
|
}
|
|
|
|
private void DisposeItem(TreeNode<TItem> node)
|
|
{
|
|
node.OnChange -= Changed;
|
|
node.Dispose();
|
|
}
|
|
|
|
protected virtual void Dispose(bool disposing)
|
|
{
|
|
if (!disposed)
|
|
{
|
|
if (disposing)
|
|
{
|
|
}
|
|
if (Nodes != null)
|
|
{
|
|
Nodes.BeginUpdate();
|
|
Nodes.ForEach(DisposeItem);
|
|
Nodes.EndUpdate();
|
|
Nodes = null;
|
|
}
|
|
disposed = true;
|
|
}
|
|
}
|
|
|
|
~TreeNode()
|
|
{
|
|
Dispose(false);
|
|
}
|
|
}
|
|
}
|