using System; using System.Collections; using System.Collections.Generic; using System.Collections.ObjectModel; using System.ComponentModel; using UnityEngine; namespace UIWidgets { [Serializable] public class ObservableList : IObservableList, IList, IObservable, ICollectionChanged, ICollectionItemChanged, IDisposable, IEnumerable, ICollection, IEnumerable { public bool ResortOnCollectionChanged = true; public bool ResortOnCollectionItemChanged = true; private Comparison comparison; [SerializeField] protected List 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 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(); } 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); } private void AddCallbacks(IEnumerable 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 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 IEnumerable.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 items) { Items.AddRange(items); AddCallbacks(items); CollectionChanged(); } public ReadOnlyCollection AsReadOnly() { return Items.AsReadOnly(); } public int BinarySearch(T item) { return Items.BinarySearch(item); } public int BinarySearch(T item, IComparer comparer) { return Items.BinarySearch(item, comparer); } public int BinarySearch(int index, int count, T item, IComparer comparer) { return Items.BinarySearch(index, count, item, comparer); } public ObservableList ConvertAll(Converter converter, bool observeItems = true) { return new ObservableList(Items.Convert(converter), observeItems); } public ObservableList Convert(Converter converter, bool observeItems = true) { return new ObservableList(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 match) { return Items.Exists(match); } public T Find(Predicate match) { return Items.Find(match); } public ObservableList FindAll(Predicate match, bool observeItems = true) { return new ObservableList(Items.FindAll(match), observeItems); } public int FindIndex(Predicate match) { return Items.FindIndex(match); } public int FindIndex(int startIndex, Predicate match) { return Items.FindIndex(startIndex, match); } public int FindIndex(int startIndex, int count, Predicate match) { return Items.FindIndex(startIndex, count, match); } public T FindLast(Predicate match) { return Items.FindLast(match); } public int FindLastIndex(Predicate match) { return Items.FindLastIndex(match); } public int FindLastIndex(int startIndex, Predicate match) { return Items.FindLastIndex(startIndex, match); } public int FindLastIndex(int startIndex, int count, Predicate match) { return Items.FindLastIndex(startIndex, count, match); } public void ForEach(Action action) { Items.ForEach(action); } public ObservableList GetRange(int index, int count, bool observeItems = true) { return new ObservableList(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 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 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 comparison) { Items.Sort(comparison); CollectionChanged(); } public void Sort(IComparer comparer) { Items.Sort(comparer); CollectionChanged(); } public void Sort(int index, int count, IComparer comparer) { Items.Sort(index, count, comparer); CollectionChanged(); } public T[] ToArray() { return Items.ToArray(); } public void TrimExcess() { Items.TrimExcess(); } public bool TrueForAll(Predicate 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); } } }