using System;
using System.Collections;
using System.Collections.Generic;
using System.Linq;
using UnityEditor;
using UnityEngine;
namespace UnityAtoms
{
///
/// Generic base class for Value Lists. Inherits from `BaseAtomList` and `IList<T>`.
///
/// The list item type.
/// Event of type `AtomEvent<T>`.
[EditorIcon("atom-icon-piglet")]
public abstract class AtomValueList : BaseAtomValueList, IList
where E : AtomEvent
{
///
/// Event for when something is added to the list.
///
public E Added;
///
/// Event for when something is removed from the list.
///
public E Removed;
///
/// Get the count of the list.
///
public int Count => list.Count;
///
/// Is the list read only?
///
public bool IsReadOnly => false;
///
/// Actual `List<T>`.
///
/// The list item type.
/// The actual `List<T>`.
[SerializeField]
protected List list = new List();
private List _initial;
#if UNITY_EDITOR
///
/// Set of all AtomVariable instances in editor.
///
private static HashSet> _instances = new HashSet>();
#endif
protected override void OnEnable()
{
base.OnEnable();
#if UNITY_EDITOR
_instances.Add(this);
EditorApplication.playModeStateChanged -= HandlePlayModeStateChange;
EditorApplication.playModeStateChanged += HandlePlayModeStateChange;
#endif
}
private void OnDisable()
{
// NOTE: This will not be called when deleting the Atom from the editor.
// Therefore, there might still be null instances, but even though not ideal,
// it should not cause any problems.
// More info: https://issuetracker.unity3d.com/issues/ondisable-and-ondestroy-methods-are-not-called-when-a-scriptableobject-is-deleted-manually-in-project-window
#if UNITY_EDITOR
_instances.Remove(this);
#endif
}
#if UNITY_EDITOR
private static void HandlePlayModeStateChange(PlayModeStateChange state)
{
if (state == PlayModeStateChange.ExitingEditMode) // BEFORE any GO is initialized:
{
foreach (var instance in _instances)
{
if (instance._startCleared) instance.list.Clear();
instance._initial = instance.list.ToList();
}
}
else if (state == PlayModeStateChange.EnteredEditMode) // AFTER Playmode stopped
{
foreach (var instance in _instances)
{
instance.list = instance._initial;
}
}
}
#endif
///
/// Add an item to the list.
///
/// The item to add.
public void Add(T item)
{
list.Add(item);
if (null != Added)
{
Added.Raise(item);
}
}
///
/// Remove an item from the list.
///
/// The item to remove.
/// True if it was removed, otherwise false..
public bool Remove(T item)
{
var removed = list.Remove(item);
if (!removed) return false;
if (null != Removed)
{
Removed.Raise(item);
}
return true;
}
///
/// Removes an item from the list.
/// This is a wrapper for `bool Remove(T item)`, useful for usage in UnityEvents.
///
/// The item to remove.
public void RemoveItem(T item) => Remove(item);
///
/// Does the list contain the item provided?
///
/// The item to check if it is contained in the list.
/// `true` if the item exists in the list, otherwise `false`.
public bool Contains(T item)
{
return list.Contains(item);
}
///
/// Get item at index.
///
/// The index.
/// The item if it exists.
public T Get(int i)
{
return list[i];
}
///
/// The actual `List<T>` as a property.
///
/// Get or set the value of the list.
public List List
{
get { return list; }
set { list = value; }
}
///
/// Indexer of the list.
///
/// Get or set an item via index in the list.
public T this[int index]
{
get
{
return list[index];
}
set
{
list[index] = value;
}
}
///
/// Copies the entire List 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 List. The Array must have zero-based indexing.
/// The zero-based index in `array` at which copying begins.
public void CopyTo(T[] array, int arrayIndex)
{
list.CopyTo(array, arrayIndex);
}
IEnumerator IEnumerable.GetEnumerator() => list.GetEnumerator();
///
/// Get an `IEnumerator<T>` of the list.
///
/// An `IEnumerator<T>`
public IEnumerator GetEnumerator() => list.GetEnumerator();
///
/// Returns the index of the specified item.
///
/// The item to search for.
/// The zero-based index of the first occurrence of `item`. If not found it returns -1.
public int IndexOf(T item) => list.IndexOf(item);
///
/// Remove an item at provided index.
///
/// The index to remove item at.
public void RemoveAt(int index)
{
var item = list[index];
list.RemoveAt(index);
if (null != Removed)
{
Removed.Raise(item);
}
}
///
/// Insert item at index.
///
/// Index to insert item at.
/// Item to insert.
public void Insert(int index, T item)
{
list.Insert(index, item);
if (Added != null)
{
Added.Raise(item);
}
}
#region Observable
///
/// Make the add event into an `IObservable<T>`. Makes Value List's add Event compatible with for example UniRx.
///
/// The add Event as an `IObservable<T>`.
public IObservable ObserveAdd()
{
if (Added == null)
{
throw new Exception("You must assign an Added event in order to observe when adding to the value list.");
}
return new ObservableEvent(Added.Register, Added.Unregister);
}
///
/// Make the remove event into an `IObservable<T>`. Makes Value List's remove Event compatible with for example UniRx.
///
/// The remove Event as an `IObservable<T>`.
public IObservable ObserveRemove()
{
if (Removed == null)
{
throw new Exception("You must assign a Removed event in order to observe when removing from the value list.");
}
return new ObservableEvent(Removed.Register, Removed.Unregister);
}
///
/// Make the clear event into an `IObservable<Void>`. Makes Value List's clear Event compatible with for example UniRx.
///
/// The clear Event as an `IObservable<Void>`.
public IObservable ObserveClear()
{
if (Cleared == null)
{
throw new Exception("You must assign a Cleared event in order to observe when clearing the value list.");
}
return new ObservableVoidEvent(Cleared.Register, Cleared.Unregister);
}
#endregion // Observable
protected override IList IList { get => List; }
}
}