571 lines
11 KiB
C#
571 lines
11 KiB
C#
using System;
|
|
using System.Collections;
|
|
using System.Collections.Generic;
|
|
using System.Collections.ObjectModel;
|
|
using System.ComponentModel;
|
|
using UnityEngine;
|
|
|
|
namespace UIWidgets
|
|
{
|
|
[Serializable]
|
|
public class ObservableList<T> : IObservableList<T>, IList<T>, IObservable, ICollectionChanged, ICollectionItemChanged, IDisposable, IEnumerable, ICollection<T>, IEnumerable<T>
|
|
{
|
|
public bool ResortOnCollectionChanged = true;
|
|
|
|
public bool ResortOnCollectionItemChanged = true;
|
|
|
|
private Comparison<T> comparison;
|
|
|
|
[SerializeField]
|
|
protected List<T> Items;
|
|
|
|
private bool IsItemsDisposable;
|
|
|
|
private bool IsItemsObservable;
|
|
|
|
private bool IsItemsSupportNotifyPropertyChanged;
|
|
|
|
private bool isCollectionChanged;
|
|
|
|
private bool isCollectionItemChanged;
|
|
|
|
private bool isChanged;
|
|
|
|
private bool inUpdate;
|
|
|
|
private bool disposed;
|
|
|
|
public Comparison<T> Comparison
|
|
{
|
|
get
|
|
{
|
|
return comparison;
|
|
}
|
|
set
|
|
{
|
|
comparison = value;
|
|
if (comparison != null)
|
|
{
|
|
if (ResortOnCollectionChanged)
|
|
{
|
|
CollectionChanged();
|
|
}
|
|
else if (ResortOnCollectionItemChanged)
|
|
{
|
|
CollectionItemChanged();
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
public int Count
|
|
{
|
|
get
|
|
{
|
|
return Items.Count;
|
|
}
|
|
}
|
|
|
|
public bool IsReadOnly
|
|
{
|
|
get
|
|
{
|
|
return false;
|
|
}
|
|
}
|
|
|
|
public T this[int index]
|
|
{
|
|
get
|
|
{
|
|
return Items[index];
|
|
}
|
|
set
|
|
{
|
|
RemoveCallback(Items[index]);
|
|
Items[index] = value;
|
|
AddCallback(Items[index]);
|
|
CollectionChanged();
|
|
}
|
|
}
|
|
|
|
public event OnChange OnChange;
|
|
|
|
public event OnChange OnCollectionChange;
|
|
|
|
public event OnChange OnCollectionItemChange;
|
|
|
|
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<T>();
|
|
}
|
|
|
|
public ObservableList(IEnumerable<T> 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<T>(enumerable);
|
|
AddCallbacks(Items);
|
|
}
|
|
|
|
private void AddCallbacks(IEnumerable<T> items)
|
|
{
|
|
items.ForEach(AddCallback);
|
|
}
|
|
|
|
private void AddCallback(T item)
|
|
{
|
|
if (IsItemsObservable)
|
|
{
|
|
(item as IObservable).OnChange += CollectionItemChanged;
|
|
}
|
|
else if (IsItemsSupportNotifyPropertyChanged)
|
|
{
|
|
(item as INotifyPropertyChanged).PropertyChanged += ItemPropertyChanged;
|
|
}
|
|
}
|
|
|
|
private void RemoveCallbacks(IEnumerable<T> items)
|
|
{
|
|
if (IsItemsObservable)
|
|
{
|
|
items.ForEach(delegate(T x)
|
|
{
|
|
(x as IObservable).OnChange -= CollectionItemChanged;
|
|
});
|
|
}
|
|
else if (IsItemsSupportNotifyPropertyChanged)
|
|
{
|
|
items.ForEach(delegate(T x)
|
|
{
|
|
(x as INotifyPropertyChanged).PropertyChanged -= ItemPropertyChanged;
|
|
});
|
|
}
|
|
}
|
|
|
|
private void RemoveCallback(T item)
|
|
{
|
|
if (IsItemsObservable)
|
|
{
|
|
(item as IObservable).OnChange -= CollectionItemChanged;
|
|
}
|
|
else if (IsItemsSupportNotifyPropertyChanged)
|
|
{
|
|
(item as INotifyPropertyChanged).PropertyChanged -= ItemPropertyChanged;
|
|
}
|
|
}
|
|
|
|
private void ItemPropertyChanged(object sender, PropertyChangedEventArgs e)
|
|
{
|
|
CollectionItemChanged();
|
|
}
|
|
|
|
public void CollectionChanged()
|
|
{
|
|
CollectionChanged(true);
|
|
}
|
|
|
|
private void CollectionChanged(bool reSort)
|
|
{
|
|
if (inUpdate)
|
|
{
|
|
isCollectionChanged = true;
|
|
isChanged = true;
|
|
return;
|
|
}
|
|
if (reSort && ResortOnCollectionChanged && comparison != null)
|
|
{
|
|
Items.Sort(comparison);
|
|
}
|
|
if (this.OnCollectionChange != null)
|
|
{
|
|
this.OnCollectionChange();
|
|
}
|
|
if (this.OnChange != null)
|
|
{
|
|
this.OnChange();
|
|
}
|
|
}
|
|
|
|
public void CollectionItemChanged()
|
|
{
|
|
if (inUpdate)
|
|
{
|
|
isCollectionItemChanged = true;
|
|
isChanged = true;
|
|
return;
|
|
}
|
|
if (ResortOnCollectionItemChanged && comparison != null)
|
|
{
|
|
Items.Sort(comparison);
|
|
}
|
|
if (this.OnCollectionItemChange != null)
|
|
{
|
|
this.OnCollectionItemChange();
|
|
}
|
|
if (this.OnChange != null)
|
|
{
|
|
this.OnChange();
|
|
}
|
|
}
|
|
|
|
[Obsolete("Use CollectionChanged() or CollectionItemChanged()")]
|
|
public void Changed()
|
|
{
|
|
if (inUpdate)
|
|
{
|
|
isChanged = true;
|
|
}
|
|
else if (this.OnChange != null)
|
|
{
|
|
this.OnChange();
|
|
}
|
|
}
|
|
|
|
public void BeginUpdate()
|
|
{
|
|
inUpdate = true;
|
|
isChanged = false;
|
|
isCollectionChanged = false;
|
|
isCollectionItemChanged = false;
|
|
}
|
|
|
|
public void EndUpdate()
|
|
{
|
|
inUpdate = false;
|
|
if (((isCollectionChanged && ResortOnCollectionItemChanged) || (isCollectionItemChanged && ResortOnCollectionItemChanged)) && comparison != null)
|
|
{
|
|
Items.Sort(comparison);
|
|
}
|
|
if (isCollectionChanged && this.OnCollectionChange != null)
|
|
{
|
|
isCollectionChanged = false;
|
|
this.OnCollectionChange();
|
|
}
|
|
if (isCollectionItemChanged && this.OnCollectionItemChange != null)
|
|
{
|
|
isCollectionItemChanged = false;
|
|
this.OnCollectionItemChange();
|
|
}
|
|
if (isChanged && this.OnChange != null)
|
|
{
|
|
isChanged = false;
|
|
this.OnChange();
|
|
}
|
|
}
|
|
|
|
public void Add(T item)
|
|
{
|
|
Items.Add(item);
|
|
AddCallback(item);
|
|
CollectionChanged();
|
|
}
|
|
|
|
public void Clear()
|
|
{
|
|
RemoveCallbacks(Items);
|
|
Items.Clear();
|
|
CollectionChanged();
|
|
}
|
|
|
|
public bool Contains(T item)
|
|
{
|
|
return Items.Contains(item);
|
|
}
|
|
|
|
public void CopyTo(T[] array, int arrayIndex)
|
|
{
|
|
Items.CopyTo(array, arrayIndex);
|
|
}
|
|
|
|
IEnumerator<T> IEnumerable<T>.GetEnumerator()
|
|
{
|
|
return Items.GetEnumerator();
|
|
}
|
|
|
|
IEnumerator IEnumerable.GetEnumerator()
|
|
{
|
|
return ((IEnumerable)Items).GetEnumerator();
|
|
}
|
|
|
|
public int IndexOf(T item)
|
|
{
|
|
return Items.IndexOf(item);
|
|
}
|
|
|
|
public void Insert(int index, T item)
|
|
{
|
|
Items.Insert(index, item);
|
|
AddCallback(item);
|
|
CollectionChanged();
|
|
}
|
|
|
|
public bool Remove(T item)
|
|
{
|
|
bool flag = Items.Remove(item);
|
|
if (flag)
|
|
{
|
|
RemoveCallback(item);
|
|
CollectionChanged();
|
|
}
|
|
return flag;
|
|
}
|
|
|
|
public void RemoveAt(int index)
|
|
{
|
|
RemoveCallback(Items[index]);
|
|
Items.RemoveAt(index);
|
|
CollectionChanged();
|
|
}
|
|
|
|
public void AddRange(IEnumerable<T> items)
|
|
{
|
|
Items.AddRange(items);
|
|
AddCallbacks(items);
|
|
CollectionChanged();
|
|
}
|
|
|
|
public ReadOnlyCollection<T> AsReadOnly()
|
|
{
|
|
return Items.AsReadOnly();
|
|
}
|
|
|
|
public int BinarySearch(T item)
|
|
{
|
|
return Items.BinarySearch(item);
|
|
}
|
|
|
|
public int BinarySearch(T item, IComparer<T> comparer)
|
|
{
|
|
return Items.BinarySearch(item, comparer);
|
|
}
|
|
|
|
public int BinarySearch(int index, int count, T item, IComparer<T> comparer)
|
|
{
|
|
return Items.BinarySearch(index, count, item, comparer);
|
|
}
|
|
|
|
public ObservableList<TOutput> ConvertAll<TOutput>(Converter<T, TOutput> converter, bool observeItems = true)
|
|
{
|
|
return new ObservableList<TOutput>(Items.Convert(converter), observeItems);
|
|
}
|
|
|
|
public ObservableList<TOutput> Convert<TOutput>(Converter<T, TOutput> converter, bool observeItems = true)
|
|
{
|
|
return new ObservableList<TOutput>(Items.Convert(converter), observeItems);
|
|
}
|
|
|
|
public void CopyTo(T[] array)
|
|
{
|
|
Items.CopyTo(array);
|
|
}
|
|
|
|
public void CopyTo(int index, T[] array, int arrayIndex, int count)
|
|
{
|
|
Items.CopyTo(index, array, arrayIndex, count);
|
|
}
|
|
|
|
public bool Exists(Predicate<T> match)
|
|
{
|
|
return Items.Exists(match);
|
|
}
|
|
|
|
public T Find(Predicate<T> match)
|
|
{
|
|
return Items.Find(match);
|
|
}
|
|
|
|
public ObservableList<T> FindAll(Predicate<T> match, bool observeItems = true)
|
|
{
|
|
return new ObservableList<T>(Items.FindAll(match), observeItems);
|
|
}
|
|
|
|
public int FindIndex(Predicate<T> match)
|
|
{
|
|
return Items.FindIndex(match);
|
|
}
|
|
|
|
public int FindIndex(int startIndex, Predicate<T> match)
|
|
{
|
|
return Items.FindIndex(startIndex, match);
|
|
}
|
|
|
|
public int FindIndex(int startIndex, int count, Predicate<T> match)
|
|
{
|
|
return Items.FindIndex(startIndex, count, match);
|
|
}
|
|
|
|
public T FindLast(Predicate<T> match)
|
|
{
|
|
return Items.FindLast(match);
|
|
}
|
|
|
|
public int FindLastIndex(Predicate<T> match)
|
|
{
|
|
return Items.FindLastIndex(match);
|
|
}
|
|
|
|
public int FindLastIndex(int startIndex, Predicate<T> match)
|
|
{
|
|
return Items.FindLastIndex(startIndex, match);
|
|
}
|
|
|
|
public int FindLastIndex(int startIndex, int count, Predicate<T> match)
|
|
{
|
|
return Items.FindLastIndex(startIndex, count, match);
|
|
}
|
|
|
|
public void ForEach(Action<T> action)
|
|
{
|
|
Items.ForEach(action);
|
|
}
|
|
|
|
public ObservableList<T> GetRange(int index, int count, bool observeItems = true)
|
|
{
|
|
return new ObservableList<T>(Items.GetRange(index, count), observeItems);
|
|
}
|
|
|
|
public int IndexOf(T item, int index)
|
|
{
|
|
return Items.IndexOf(item, index);
|
|
}
|
|
|
|
public int IndexOf(T item, int index, int count)
|
|
{
|
|
return Items.IndexOf(item, index, count);
|
|
}
|
|
|
|
public void InsertRange(int index, IEnumerable<T> collection)
|
|
{
|
|
Items.InsertRange(index, collection);
|
|
AddCallbacks(collection);
|
|
CollectionChanged();
|
|
}
|
|
|
|
public int LastIndexOf(T item)
|
|
{
|
|
return Items.LastIndexOf(item);
|
|
}
|
|
|
|
public int LastIndexOf(T item, int index)
|
|
{
|
|
return Items.LastIndexOf(item, index);
|
|
}
|
|
|
|
public int LastIndexOf(T item, int index, int count)
|
|
{
|
|
return Items.LastIndexOf(item, index, count);
|
|
}
|
|
|
|
public int RemoveAll(Predicate<T> match)
|
|
{
|
|
RemoveCallbacks(Items.FindAll(match));
|
|
int result = Items.RemoveAll(match);
|
|
CollectionChanged();
|
|
return result;
|
|
}
|
|
|
|
public void RemoveRange(int index, int count)
|
|
{
|
|
RemoveCallbacks(Items.GetRange(index, count));
|
|
Items.RemoveRange(index, count);
|
|
CollectionChanged();
|
|
}
|
|
|
|
public void Reverse()
|
|
{
|
|
Items.Reverse();
|
|
CollectionChanged();
|
|
}
|
|
|
|
public void Reverse(int index, int count)
|
|
{
|
|
Items.Reverse(index, count);
|
|
CollectionChanged();
|
|
}
|
|
|
|
public void Sort()
|
|
{
|
|
Items.Sort();
|
|
CollectionChanged();
|
|
}
|
|
|
|
public void Sort(Comparison<T> comparison)
|
|
{
|
|
Items.Sort(comparison);
|
|
CollectionChanged();
|
|
}
|
|
|
|
public void Sort(IComparer<T> comparer)
|
|
{
|
|
Items.Sort(comparer);
|
|
CollectionChanged();
|
|
}
|
|
|
|
public void Sort(int index, int count, IComparer<T> comparer)
|
|
{
|
|
Items.Sort(index, count, comparer);
|
|
CollectionChanged();
|
|
}
|
|
|
|
public T[] ToArray()
|
|
{
|
|
return Items.ToArray();
|
|
}
|
|
|
|
public void TrimExcess()
|
|
{
|
|
Items.TrimExcess();
|
|
}
|
|
|
|
public bool TrueForAll(Predicate<T> match)
|
|
{
|
|
return Items.TrueForAll(match);
|
|
}
|
|
|
|
public void Dispose()
|
|
{
|
|
Dispose(true);
|
|
GC.SuppressFinalize(this);
|
|
}
|
|
|
|
private void DisposeItem(T item)
|
|
{
|
|
RemoveCallback(item);
|
|
if (IsItemsDisposable)
|
|
{
|
|
(item as IDisposable).Dispose();
|
|
}
|
|
}
|
|
|
|
protected virtual void Dispose(bool disposing)
|
|
{
|
|
ResortOnCollectionChanged = false;
|
|
ResortOnCollectionItemChanged = false;
|
|
if (!disposed)
|
|
{
|
|
if (disposing)
|
|
{
|
|
}
|
|
if (Items != null)
|
|
{
|
|
BeginUpdate();
|
|
Items.ForEach(DisposeItem);
|
|
EndUpdate();
|
|
Items = null;
|
|
}
|
|
disposed = true;
|
|
}
|
|
}
|
|
|
|
~ObservableList()
|
|
{
|
|
Dispose(false);
|
|
}
|
|
}
|
|
}
|