using UnityEngine;
using System.Collections;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System;
using System.ComponentModel;
namespace UIWidgets {
[Serializable]
///
/// Observable list.
///
public class ObservableList : IObservableList, IList {
///
/// Occurs when data changed.
///
public event OnChange OnChange;
///
/// Occurs when changed collection (added item, removed item, replaced item).
///
public event OnChange OnCollectionChange;
///
/// Occurs when changed data of item in collection.
///
public event OnChange OnCollectionItemChange;
///
/// Re-sort items when collection changed.
///
public bool ResortOnCollectionChanged = true;
///
/// Re-sort item when collection item changed.
///
public bool ResortOnCollectionItemChanged = true;
Comparison comparison;
///
/// Sorts the elements in the entire ObservableList using the specified System.Comparison.
///
/// The comparison.
public Comparison Comparison {
get {
return comparison;
}
set {
comparison = value;
if (comparison!=null)
{
if (ResortOnCollectionChanged)
{
CollectionChanged();
}
else if (ResortOnCollectionItemChanged)
{
CollectionItemChanged();
}
}
}
}
///
/// The items.
///
[SerializeField]
protected List Items;
bool IsItemsDisposable;
bool IsItemsObservable;
bool IsItemsSupportNotifyPropertyChanged;
///
/// Initializes a new instance of the class.
///
/// Is need to observe items?
public ObservableList(bool observeItems = true)
{
IsItemsDisposable = observeItems && typeof(IDisposable).IsAssignableFrom(typeof(T));
IsItemsObservable = observeItems && typeof(IObservable).IsAssignableFrom(typeof(T));
IsItemsSupportNotifyPropertyChanged = typeof(INotifyPropertyChanged).IsAssignableFrom(typeof(T));
Items = new List();
}
///
/// Initializes a new instance of the class.
///
/// Enumerable.
/// Is need to observe items? If true ObservableList.OnChange will be raised on item OnChange or PropertyChanged.
public ObservableList(IEnumerable enumerable, bool observeItems = true)
{
IsItemsDisposable = observeItems && typeof(IDisposable).IsAssignableFrom(typeof(T));
IsItemsObservable = observeItems && typeof(IObservable).IsAssignableFrom(typeof(T));
IsItemsSupportNotifyPropertyChanged = typeof(INotifyPropertyChanged).IsAssignableFrom(typeof(T));
Items = new List(enumerable);
AddCallbacks(Items);
}
void AddCallbacks(IEnumerable items)
{
items.ForEach(AddCallback);
}
void AddCallback(T item)
{
if (IsItemsObservable)
{
(item as IObservable).OnChange += CollectionItemChanged;
}
else if (IsItemsSupportNotifyPropertyChanged)
{
(item as INotifyPropertyChanged).PropertyChanged += ItemPropertyChanged;
}
}
void RemoveCallbacks(IEnumerable items)
{
if (IsItemsObservable)
{
items.ForEach(x => (x as IObservable).OnChange -= CollectionItemChanged);
}
else if (IsItemsSupportNotifyPropertyChanged)
{
items.ForEach(x => (x as INotifyPropertyChanged).PropertyChanged -= ItemPropertyChanged);
}
}
void RemoveCallback(T item)
{
if (IsItemsObservable)
{
(item as IObservable).OnChange -= CollectionItemChanged;
}
else if (IsItemsSupportNotifyPropertyChanged)
{
(item as INotifyPropertyChanged).PropertyChanged -= ItemPropertyChanged;
}
}
void ItemPropertyChanged(object sender, PropertyChangedEventArgs e)
{
CollectionItemChanged();
}
///
/// Changed collection.
///
public void CollectionChanged()
{
CollectionChanged(true);
}
void CollectionChanged(bool reSort)
{
if (inUpdate)
{
isCollectionChanged = true;
isChanged = true;
}
else
{
if (reSort && ResortOnCollectionChanged && (comparison!=null))
{
Items.Sort(comparison);
}
if (OnCollectionChange!=null)
{
OnCollectionChange();
}
if (OnChange!=null)
{
OnChange();
}
}
}
///
/// Changed data of item in collection.
///
public void CollectionItemChanged()
{
if (inUpdate)
{
isCollectionItemChanged = true;
isChanged = true;
}
else
{
if (ResortOnCollectionItemChanged && (comparison!=null))
{
Items.Sort(comparison);
}
if (OnCollectionItemChange!=null)
{
OnCollectionItemChange();
}
if (OnChange!=null)
{
OnChange();
}
}
}
///
/// Changed this instance.
///
[Obsolete("Use CollectionChanged() or CollectionItemChanged()")]
public void Changed()
{
if (inUpdate)
{
isChanged = true;
}
else
{
if (OnChange!=null)
{
OnChange();
}
}
}
bool isCollectionChanged;
bool isCollectionItemChanged;
bool isChanged;
bool inUpdate;
///
/// Maintains performance while items are added/removed/changed by preventing the widgets from drawing until the EndUpdate method is called.
///
public void BeginUpdate()
{
inUpdate = true;
isChanged = false;
isCollectionChanged = false;
isCollectionItemChanged = false;
}
///
/// Ends the update and raise OnChange if something was changed.
///
public void EndUpdate()
{
inUpdate = false;
if ((isCollectionChanged && ResortOnCollectionItemChanged) || (isCollectionItemChanged && ResortOnCollectionItemChanged))
{
if (comparison!=null)
{
Items.Sort(comparison);
}
}
if (isCollectionChanged && (OnCollectionChange!=null))
{
isCollectionChanged = false;
OnCollectionChange();
}
if (isCollectionItemChanged && (OnCollectionItemChange!=null))
{
isCollectionItemChanged = false;
OnCollectionItemChange();
}
if (isChanged && (OnChange!=null))
{
isChanged = false;
OnChange();
}
}
///
/// Gets the count.
///
/// The count.
public int Count {
get {
return Items.Count;
}
}
///
/// Gets a value indicating whether this instance is read only.
///
/// true if this instance is read only; otherwise, false.
public bool IsReadOnly {
get {
return false;
}
}
///
/// Gets or sets the element at the specified index.
///
/// The element at the specified index.
/// The zero-based index of the element to get or set.
public T this[int index] {
get {
return Items[index];
}
set {
RemoveCallback(Items[index]);
Items[index] = value;
AddCallback(Items[index]);
CollectionChanged();
}
}
///
/// Adds an object to the end of the ObservableList.
///
/// The object to be added to the end of the ObservableList. The value can be null for reference types.
public void Add(T item)
{
Items.Add(item);
AddCallback(item);
CollectionChanged();
}
///
/// Removes all elements from the ObservableList.
///
public void Clear()
{
RemoveCallbacks(Items);
Items.Clear();
CollectionChanged();
}
///
/// Determines whether an element is in the ObservableList.
///
/// true if item is found in the ObservableList; otherwise, false.
/// The object to locate in the ObservableList. The value can be null for reference types.
public bool Contains(T item)
{
return Items.Contains(item);
}
///
/// Copies the entire ObservableList to a compatible one-dimensional array, starting at the specified index of the target array.
///
/// The one-dimensional Array that is the destination of the elements copied from ObservableList. The Array must have zero-based indexing.
/// The zero-based index in array at which copying begins.
public void CopyTo(T[] array, int arrayIndex)
{
Items.CopyTo(array, arrayIndex);
}
///
/// Returns an enumerator that iterates through the ObservableList.
///
/// A ObservableList.Enumerator for the ObservableList.
IEnumerator IEnumerable.GetEnumerator()
{
return Items.GetEnumerator();
}
///
/// Returns an enumerator that iterates through a collection.
///
/// An IEnumerator object that can be used to iterate through the collection.
IEnumerator IEnumerable.GetEnumerator()
{
return (Items as IEnumerable).GetEnumerator();
}
///
/// Searches for the specified object and returns the zero-based index of the first occurrence within the entire ObservableList.
///
/// The zero-based index of the first occurrence of item within the entire ObservableList, if found; otherwise, –1.
/// The object to locate in the ObservableList. The value can be null for reference types.
public int IndexOf(T item)
{
return Items.IndexOf(item);
}
///
/// Inserts an element into the ObservableList at the specified index.
///
/// The zero-based index at which item should be inserted.
/// The object to insert. The value can be null for reference types.
public void Insert(int index, T item)
{
Items.Insert(index, item);
AddCallback(item);
CollectionChanged();
}
///
/// Removes the first occurrence of a specific object from the ObservableList.
///
/// true if item is successfully removed; otherwise, false. This method also returns false if item was not found in the ObservableList.
/// The object to remove from the ObservableList. The value can be null for reference types.
public bool Remove(T item)
{
var result = Items.Remove(item);
if (result)
{
RemoveCallback(item);
CollectionChanged();
}
return result;
}
///
/// Removes the element at the specified index of the ObservableList.
///
/// The zero-based index of the element to remove.
public void RemoveAt(int index)
{
RemoveCallback(Items[index]);
Items.RemoveAt(index);
CollectionChanged();
}
///
/// Adds the elements of the specified collection to the end of the ObservableList.
///
/// The collection whose elements should be added to the end of the ObservableList. The collection itself cannot be null, but it can contain elements that are null, if type T is a reference type.
public void AddRange(IEnumerable items)
{
Items.AddRange(items);
AddCallbacks(items);
CollectionChanged();
}
///
/// Returns a read-only IList wrapper for the current collection.
///
/// TA ReadOnlyCollection that acts as a read-only wrapper around the current ObservableList.
public ReadOnlyCollection AsReadOnly()
{
#if NETFX_CORE
return new System.Collections.ObjectModel.ReadOnlyCollection(Items);
#else
return Items.AsReadOnly();
#endif
}
///
/// Searches the entire sorted ObservableList for an element using the default comparer and returns the zero-based index of the element.
///
/// The zero-based index of item in the sorted ObservableList, if item is found; otherwise, a negative number that is the bitwise complement of the index of the next element that is larger than item or, if there is no larger element, the bitwise complement of Count.
/// The object to locate. The value can be null for reference types.
public int BinarySearch(T item)
{
return Items.BinarySearch(item);
}
///
/// Searches the entire sorted ObservableList for an element using the specified comparer and returns the zero-based index of the element.
///
/// The zero-based index of item in the sorted ObservableList, if item is found; otherwise, a negative number that is the bitwise complement of the index of the next element that is larger than item or, if there is no larger element, the bitwise complement of Count.
/// The object to locate. The value can be null for reference types.
/// The IComparer implementation to use when comparing elements, or null to use the default comparer Comparer.Default.
public int BinarySearch(T item, IComparer comparer)
{
return Items.BinarySearch(item, comparer);
}
///
/// Searches a range of elements in the sorted ObservableList for an element using the specified comparer and returns the zero-based index of the element.
///
/// The zero-based index of item in the sorted ObservableList, if item is found; otherwise, a negative number that is the bitwise complement of the index of the next element that is larger than item or, if there is no larger element, the bitwise complement of Count.
/// The zero-based starting index of the range to search.
/// The length of the range to search.
/// The object to locate. The value can be null for reference types.
/// The IComparer implementation to use when comparing elements, or null to use the default comparer Comparer.Default.
public int BinarySearch(int index, int count, T item, IComparer comparer)
{
return Items.BinarySearch(index, count, item, comparer);
}
///
/// Searches the entire sorted ObservableList for an element using the specified comparison and returns the zero-based index of the element.
///
/// The zero-based index of item in the sorted ObservableList, if item is found; otherwise, a negative number that is the bitwise complement of the index of the next element that is larger than item or, if there is no larger element, the bitwise complement of Count.
/// The object to locate. The value can be null for reference types.
/// The Comparison implementation to use when comparing elements, or null to use the default comparer Comparer.Default.
public int BinarySearch(T item, Comparison comparison)
{
if (comparison==null)
{
return BinarySearch(item);
}
return BinarySearch(item, new ComparisonComparer(comparison));
}
///
/// Converts the elements in the current ObservableList to another type, and returns a list containing the converted elements.
///
/// A ObservableList of the target type containing the converted elements from the current ObservableList.
/// A Converter delegate that converts each element from one type to another type.
/// Is need to observe items?
/// The type of the elements of the target array.
public ObservableList ConvertAll(Converter converter, bool observeItems = true)
{
return new ObservableList(Items.Convert(converter), observeItems);
}
///
/// Converts the elements in the current ObservableList to another type, and returns a list containing the converted elements.
///
/// A ObservableList of the target type containing the converted elements from the current ObservableList.
/// A Converter delegate that converts each element from one type to another type.
/// Is need to observe items?
/// The type of the elements of the target array.
public ObservableList Convert(Converter converter, bool observeItems = true)
{
return new ObservableList(Items.Convert(converter), observeItems);
}
///
/// Copies the entire ObservableList to a compatible one-dimensional array, starting at the beginning of the target array.
///
/// The one-dimensional Array that is the destination of the elements copied from ObservableList. The Array must have zero-based indexing.
public void CopyTo(T[] array)
{
Items.CopyTo(array);
}
///
/// Copies a range of elements from the ObservableList to a compatible one-dimensional array, starting at the specified index of the target array.
///
/// The zero-based index in the source ObservableList at which copying begins.
/// The one-dimensional Array that is the destination of the elements copied from ObservableList. The Array must have zero-based indexing.
/// The zero-based index in array at which copying begins.
/// The number of elements to copy.
public void CopyTo(int index, T[] array, int arrayIndex, int count)
{
Items.CopyTo(index, array, arrayIndex, count);
}
///
/// Determines whether the ObservableList contains elements that match the conditions defined by the specified predicate.
///
/// true if the ObservableList contains one or more elements that match the conditions defined by the specified predicate; otherwise, false.
/// The Predicate delegate that defines the conditions of the elements to search for.
public bool Exists(Predicate match)
{
return Items.Exists(match);
}
///
/// Searches for an element that matches the conditions defined by the specified predicate, and returns the first occurrence within the entire ObservableList.
///
/// The first element that matches the conditions defined by the specified predicate, if found; otherwise, the default value for type T.
/// The Predicate delegate that defines the conditions of the element to search for.
public T Find(Predicate match)
{
return Items.Find(match);
}
///
/// Retrieves all the elements that match the conditions defined by the specified predicate.
///
/// A ObservableList containing all the elements that match the conditions defined by the specified predicate, if found; otherwise, an empty ObservableList.
/// The Predicate delegate that defines the conditions of the elements to search for.
/// Is need to observe items?
public ObservableList FindAll(Predicate match, bool observeItems = true)
{
return new ObservableList(Items.FindAll(match), observeItems);
}
///
/// Searches for an element that matches the conditions defined by the specified predicate, and returns the zero-based index of the first occurrence within the entire ObservableList.
///
/// The zero-based index of the first occurrence of an element that matches the conditions defined by match, if found; otherwise, –1.
/// The Predicate delegate that defines the conditions of the element to search for.
public int FindIndex(Predicate match)
{
return Items.FindIndex(match);
}
///
/// Searches for an element that matches the conditions defined by the specified predicate, and returns the zero-based index of the first occurrence within the range of elements in the ObservableList that extends from the specified index to the last element.
///
/// The zero-based index of the first occurrence of an element that matches the conditions defined by match, if found; otherwise, –1.
/// The zero-based starting index of the search.
/// The Predicate delegate that defines the conditions of the element to search for.
public int FindIndex(int startIndex, Predicate match)
{
return Items.FindIndex(startIndex, match);
}
///
/// Searches for an element that matches the conditions defined by the specified predicate, and returns the zero-based index of the first occurrence within the range of elements in the ObservableList that starts at the specified index and contains the specified number of elements.
///
/// The zero-based index of the first occurrence of an element that matches the conditions defined by match, if found; otherwise, –1.
/// The zero-based starting index of the search.
/// The number of elements in the section to search.
/// The Predicate delegate that defines the conditions of the element to search for.
public int FindIndex(int startIndex, int count, Predicate match)
{
return Items.FindIndex(startIndex, count, match);
}
///
/// Searches for an element that matches the conditions defined by the specified predicate, and returns the last occurrence within the entire ObservableList.
///
/// The last element that matches the conditions defined by the specified predicate, if found; otherwise, the default value for type T.
/// The Predicate delegate that defines the conditions of the element to search for.
public T FindLast(Predicate match)
{
return Items.FindLast(match);
}
///
/// Searches for an element that matches the conditions defined by the specified predicate, and returns the zero-based index of the last occurrence within the entire ObservableList.
///
/// The zero-based index of the last occurrence of an element that matches the conditions defined by match, if found; otherwise, –1.
/// The Predicate delegate that defines the conditions of the element to search for.
public int FindLastIndex(Predicate match)
{
return Items.FindLastIndex(match);
}
///
/// Searches for an element that matches the conditions defined by the specified predicate, and returns the zero-based index of the last occurrence within the range of elements in the ObservableList that extends from the first element to the specified index.
///
/// The zero-based index of the last occurrence of an element that matches the conditions defined by match, if found; otherwise, –1.
/// The zero-based starting index of the backward search.
/// The Predicate delegate that defines the conditions of the element to search for.
public int FindLastIndex(int startIndex, Predicate match)
{
return Items.FindLastIndex(startIndex, match);
}
///
/// Searches for an element that matches the conditions defined by the specified predicate, and returns the zero-based index of the last occurrence within the range of elements in the ObservableList that contains the specified number of elements and ends at the specified index.
///
/// The zero-based index of the last occurrence of an element that matches the conditions defined by match, if found; otherwise, –1.
/// The zero-based starting index of the backward search.
/// The number of elements in the section to search.
/// The Predicate delegate that defines the conditions of the element to search for.
public int FindLastIndex(int startIndex, int count, Predicate match)
{
return Items.FindLastIndex(startIndex, count, match);
}
///
/// Performs the specified action on each element of the ObservableList.
///
/// The Action delegate to perform on each element of the ObservableList.
public void ForEach(Action action)
{
Items.ForEach(action);
}
///
/// Creates a shallow copy of a range of elements in the source ObservableList.
///
/// A shallow copy of a range of elements in the source ObservableList.
/// The zero-based ObservableList index at which the range starts.
/// The number of elements in the range.
/// Is need to observe items?
public ObservableList GetRange(int index, int count, bool observeItems = true)
{
return new ObservableList(Items.GetRange(index, count), observeItems);
}
///
/// Searches for the specified object and returns the zero-based index of the first occurrence within the range of elements in the ObservableList that extends from the specified index to the last element.
///
/// The zero-based index of the first occurrence of item within the range of elements in the ObservableList that extends from index to the last element, if found; otherwise, –1.
/// The object to locate in the ObservableList. The value can be null for reference types.
/// The zero-based starting index of the search. 0 (zero) is valid in an empty list.
public int IndexOf(T item, int index)
{
return Items.IndexOf(item, index);
}
///
/// Searches for the specified object and returns the zero-based index of the first occurrence within the range of elements in the ObservableList that starts at the specified index and contains the specified number of elements.
///
/// The zero-based index of the first occurrence of item within the range of elements in the ObservableList that starts at index and contains count number of elements, if found; otherwise, –1.
/// The object to locate in the ObservableList. The value can be null for reference types.
/// The zero-based starting index of the search. 0 (zero) is valid in an empty list.
/// The number of elements in the section to search.
public int IndexOf(T item, int index, int count)
{
return Items.IndexOf(item, index, count);
}
///
/// Inserts the elements of a collection into the ObservableList at the specified index.
///
/// The zero-based index at which the new elements should be inserted.
/// The collection whose elements should be inserted into the ObservableList. The collection itself cannot be null, but it can contain elements that are null, if type T is a reference type.
public void InsertRange(int index, IEnumerable collection)
{
Items.InsertRange(index, collection);
AddCallbacks(collection);
CollectionChanged();
}
///
/// Searches for the specified object and returns the zero-based index of the last occurrence within the entire ObservableList.
///
/// The zero-based index of the last occurrence of item within the entire the ObservableList, if found; otherwise, –1.
/// The object to locate in the ObservableList. The value can be null for reference types.
public int LastIndexOf(T item)
{
return Items.LastIndexOf(item);
}
///
/// Searches for the specified object and returns the zero-based index of the last occurrence within the range of elements in the ObservableList that extends from the first element to the specified index.
///
/// The zero-based index of the last occurrence of item within the range of elements in the ObservableList that extends from the first element to index, if found; otherwise, –1.
/// The object to locate in the ObservableList. The value can be null for reference types.
/// The zero-based starting index of the backward search.
public int LastIndexOf(T item, int index)
{
return Items.LastIndexOf(item, index);
}
///
/// Searches for the specified object and returns the zero-based index of the last occurrence within the range of elements in the ObservableList that contains the specified number of elements and ends at the specified index.
///
/// The zero-based index of the last occurrence of item within the range of elements in the ObservableList that contains count number of elements and ends at index, if found; otherwise, –1.
/// The object to locate in the ObservableList. The value can be null for reference types.
/// The zero-based starting index of the backward search.
/// The number of elements in the section to search.
public int LastIndexOf(T item, int index, int count)
{
return Items.LastIndexOf(item, index, count);
}
///
/// Removes all the elements that match the conditions defined by the specified predicate.
///
/// The number of elements removed from the ObservableList.
/// The Predicate delegate that defines the conditions of the elements to remove.
public int RemoveAll(Predicate match)
{
RemoveCallbacks(Items.FindAll(match));
var deleted = Items.RemoveAll(match);
CollectionChanged();
return deleted;
}
///
/// Removes a range of elements from the ObservableList.
///
/// The zero-based starting index of the range of elements to remove.
/// The number of elements to remove.
public void RemoveRange(int index, int count)
{
RemoveCallbacks(Items.GetRange(index, count));
Items.RemoveRange(index, count);
CollectionChanged();
}
///
/// Reverses the order of the elements in the entire ObservableList.
///
public void Reverse()
{
Items.Reverse();
CollectionChanged();
}
///
/// Reverses the order of the elements in the specified range.
///
/// The zero-based starting index of the range to reverse.
/// The number of elements in the range to reverse.
public void Reverse(int index, int count)
{
Items.Reverse(index, count);
CollectionChanged();
}
///
/// Sorts the elements in the entire ObservableList using the default comparer.
///
public void Sort()
{
Items.Sort();
CollectionChanged();
}
///
/// Sorts the elements in the entire ObservableList using the specified System.Comparison.
///
/// The System.Comparison to use when comparing elements.
public void Sort(Comparison comparison)
{
Items.Sort(comparison);
CollectionChanged();
}
///
/// Sorts the elements in the entire ObservableList using the specified comparer.
///
/// The IComparer implementation to use when comparing elements, or null to use the default comparer Comparer.Default.
public void Sort(IComparer comparer)
{
Items.Sort(comparer);
CollectionChanged();
}
///
/// Sorts the elements in a range of elements in ObservableList using the specified comparer.
///
/// The zero-based starting index of the range to sort.
/// The length of the range to sort.
/// The IComparer implementation to use when comparing elements, or null to use the default comparer Comparer.Default.
public void Sort(int index, int count, IComparer comparer)
{
Items.Sort(index, count, comparer);
CollectionChanged();
}
///
/// Copies the elements of the ObservableList to a new array.
///
/// An array containing copies of the elements of the ObservableList.
public T[] ToArray()
{
return Items.ToArray();
}
///
/// Sets the capacity to the actual number of elements in the ObservableList, if that number is less than a threshold value.
///
public void TrimExcess()
{
Items.TrimExcess();
}
///
/// Determines whether every element in the ObservableList matches the conditions defined by the specified predicate.
///
/// true if every element in the ObservableList matches the conditions defined by the specified predicate; otherwise, false. If the list has no elements, the return value is true.
/// The Predicate delegate that defines the conditions to check against the elements.
public bool TrueForAll(Predicate match)
{
return Items.TrueForAll(match);
}
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(T item)
{
RemoveCallback(item);
if (IsItemsDisposable)
{
(item as IDisposable).Dispose();
}
}
///
/// Dispose.
///
/// Free other state (managed objects).
protected virtual void Dispose(bool disposing)
{
ResortOnCollectionChanged = false;
ResortOnCollectionItemChanged = false;
if (!disposed)
{
if (disposing)
{
// Free other state (managed objects).
}
if (Items!=null)
{
BeginUpdate();
Items.ForEach(DisposeItem);
EndUpdate();
Items = null;
}
// Free your own state (unmanaged objects).
// Set large fields to null.
disposed = true;
}
}
// Use C# destructor syntax for finalization code.
~ObservableList()
{
Dispose(false);
}
}
}