天津23维预案
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 
 

476 lines
11 KiB

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