using UnityEngine;
using System.Linq;
using System.Collections.Generic;
using System;
using System.ComponentModel;
namespace UIWidgets {
///
/// Tree node.
///
[Serializable]
public class TreeNode : IObservable, IDisposable, INotifyPropertyChanged
{
///
/// Occurs when on change.
///
public event OnChange OnChange;
///
/// Occurs when a property value changes.
///
public event PropertyChangedEventHandler PropertyChanged;
///
/// The pause observation.
///
public bool PauseObservation;
[SerializeField]
bool isVisible = true;
///
/// Gets or sets a value indicating whether this instance is visible.
///
/// true if this instance is visible; otherwise, false.
public bool IsVisible {
get {
return isVisible;
}
set {
isVisible = value;
Changed("IsVisible");
}
}
[SerializeField]
bool isExpanded;
///
/// Gets or sets a value indicating whether this instance is expanded.
///
/// true if this instance is expanded; otherwise, false.
public bool IsExpanded {
get {
return isExpanded;
}
set {
isExpanded = value;
Changed("IsExpanded");
}
}
[SerializeField]
TItem item;
///
/// Gets or sets the item.
///
/// The item.
public TItem Item {
get {
return item;
}
set {
item = value;
Changed("Item");
}
}
[SerializeField]
IObservableList> nodes;
///
/// Gets or sets the nodes.
///
/// The nodes.
public IObservableList> 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");
}
}
///
/// Gets the total nodes count.
///
/// The total nodes count.
public int TotalNodesCount {
get {
if (nodes==null)
{
return 1;
}
return nodes.Sum(x => x.TotalNodesCount) + 1;
}
}
///
/// The used nodes count.
///
public int UsedNodesCount;
///
/// Gets all used nodes count.
///
/// All used nodes count.
public int AllUsedNodesCount {
get {
if (!isVisible)
{
return 0;
}
if (!isExpanded)
{
return 0 + UsedNodesCount;
}
if (nodes==null)
{
return 0 + UsedNodesCount;
}
return nodes.Sum(x => x.AllUsedNodesCount) + UsedNodesCount;
}
}
WeakReference parent;
///
/// Gets or sets the parent.
///
/// The parent.
public TreeNode Parent {
get {
if ((parent!=null) && (parent.IsAlive))
{
return parent.Target as TreeNode;
}
return null;
}
set {
SetParentValue(value);
}
}
public List> Path {
get {
var result = new List>();
var current_parent = Parent;
while (current_parent!=null)
{
result.Add(current_parent);
current_parent = current_parent.Parent;
}
var last = result.Count - 1;
if ((last>=0) && (result[last].Item==null))
{
result.RemoveAt(last);
}
return result;
}
}
///
/// Determines whether this instance is parent of node the specified node.
///
/// true if this instance is parent of node the specified node; otherwise, false.
/// Node.
public bool IsParentOfNode(TreeNode node)
{
var nodeParent = node.Parent;
while (nodeParent != null)
{
if (nodeParent==this)
{
return true;
}
nodeParent = nodeParent.Parent;
}
return false;
}
///
/// Determines whether this instance can be child of the specified newParent.
///
/// true if this instance can be child of the specified newParent; otherwise, false.
/// New parent.
public bool CanBeParent(TreeNode newParent)
{
if (this==newParent)
{
return false;
}
return !IsParentOfNode(newParent);
}
void SetParentValue(TreeNode newParent)
{
var oldParent = ((parent!=null) && (parent.IsAlive)) ? parent.Target as TreeNode : null;
if (oldParent==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 (oldParent!=null)
{
oldParent.nodes.OnCollectionChange -= oldParent.CollectionChanged;
oldParent.nodes.Remove(this);
oldParent.nodes.OnCollectionChange += oldParent.CollectionChanged;
}
parent = new WeakReference(newParent);
if (newParent!=null)
{
if (newParent.nodes==null)
{
newParent.nodes = new ObservableList>();
newParent.nodes.OnChange += newParent.Changed;
newParent.nodes.OnCollectionChange += newParent.CollectionChanged;
}
newParent.nodes.OnCollectionChange -= newParent.CollectionChanged;
newParent.nodes.Add(this);
newParent.nodes.OnCollectionChange += newParent.CollectionChanged;
}
//Changed();
}
///
/// Initializes a new instance of the class.
///
/// Node item.
/// Node nodes.
/// If set to true node is expanded.
/// If set to true node is visible.
public TreeNode(TItem nodeItem,
IObservableList> 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();
}
}
void CollectionChanged()
{
if (nodes==null)
{
return ;
}
nodes.ForEach(SetParent);
}
void SetParent(TreeNode node)
{
if ((node.Parent!=null) && (node.Parent!=this))
{
node.Parent.nodes.Remove(node);
}
node.parent = new WeakReference(this);
}
void Changed()
{
Changed("Nodes");
}
void Changed(string propertyName)
{
if (PauseObservation)
{
return ;
}
if (OnChange!=null)
{
OnChange();
}
if (PropertyChanged!=null)
{
PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
}
}
///
/// Determines whether the specified object is equal to the current object.
///
/// The object to compare with the current object.
/// true if the specified object is equal to the current object; otherwise, false.
public override bool Equals(System.Object obj)
{
var nodeObj = obj as TreeNode;
if (nodeObj==null)
{
return this==null;
}
if (this==null)
{
return false;
}
return item.Equals(nodeObj.item);
}
///
/// Serves as a hash function for a particular type.
///
/// A hash code for this instance that is suitable for use in hashing algorithms and data structures such as a hash table.
public override int GetHashCode()
{
return base.GetHashCode();
}
///
/// Returns true if the nodes items are equal, false otherwise.
///
/// The alpha component.
/// The blue component.
public static bool operator ==(TreeNode a, TreeNode b)
{
var a_null = object.ReferenceEquals(null, a);
var b_null = object.ReferenceEquals(null, b);
if (a_null && b_null)
{
return true;
}
if (a_null!=b_null)
{
return false;
}
var a_item_null = object.ReferenceEquals(null, a.item);
var b_item_null = object.ReferenceEquals(null, b.item);
if (a_item_null && b_item_null)
{
return true;
}
if (a_item_null!=b_item_null)
{
return false;
}
return a.item.Equals(b.item);
}
///
/// Returns true if the nodes items are not equal, false otherwise.
///
/// The alpha component.
/// The blue component.
public static bool operator !=(TreeNode a, TreeNode b)
{
var a_null = object.ReferenceEquals(null, a);
var b_null = object.ReferenceEquals(null, b);
if (a_null && b_null)
{
return false;
}
if (a_null!=b_null)
{
return true;
}
var a_item_null = object.ReferenceEquals(null, a.item);
var b_item_null = object.ReferenceEquals(null, b.item);
if (a_item_null && b_item_null)
{
return false;
}
if (a_item_null!=b_item_null)
{
return true;
}
return !a.item.Equals(b.item);
}
private bool disposed = false;
///
/// Performs application-defined tasks associated with freeing, releasing, or resetting unmanaged resources.
///
/// Call when you are finished using the . The method leaves the in an unusable state. After calling , you must release all references to the so the garbage collector can reclaim the memory that the was occupying.
public void Dispose()
{
Dispose(true);
GC.SuppressFinalize(this);
}
void DisposeItem(TreeNode node)
{
node.OnChange -= Changed;
node.Dispose();
}
///
/// Dispose.
///
/// Free other state (managed objects).
protected virtual void Dispose(bool disposing)
{
if (!disposed)
{
if (disposing)
{
// Free other state (managed objects).
}
if (Nodes!=null)
{
Nodes.BeginUpdate();
Nodes.ForEach(DisposeItem);
Nodes.EndUpdate();
Nodes = null;
}
// Free your own state (unmanaged objects).
// Set large fields to null.
disposed = true;
}
}
// Use C# destructor syntax for finalization code.
~TreeNode()
{
// Simply call Dispose(false).
Dispose(false);
}
}
}