namespace Zinnia.Data.Type.Observer { using System.Collections.Generic; using UnityEngine; using UnityEngine.Events; using Zinnia.Extension; /// /// The basis for all Observable Property types. /// public abstract class ObservableProperty : MonoBehaviour { } /// /// Allows observing changes to a property. /// /// The data type. /// The type to use. public abstract class ObservableProperty : ObservableProperty where TEvent : UnityEvent, new() { /// /// Emitted when the property value is set and modified from its previous value. /// [Header("Observable Events")] public TEvent Modified = new TEvent(); /// /// Emitted when the property value is set and unmodified from its previous value. /// public TEvent Unmodified = new TEvent(); /// /// Emitted when the property value is set and modified back to the data type's default value. /// public TEvent Defaulted = new TEvent(); /// /// Emitted when the property value is set and modified from the data type's default value to a defined value. /// public TEvent Defined = new TEvent(); [Header("Property Settings")] [Tooltip("The observed data.")] [SerializeField] private TType data; /// /// The observed data. /// public TType Data { get { return data; } set { if (this.IsMemberChangeAllowed()) { OnBeforeDataChange(); } data = value; if (this.IsMemberChangeAllowed()) { OnAfterDataChange(); } } } [Tooltip("Whether to observe data changes that were made when the component was disabled and subsequently re-enabled. Events are not raised when component is disabled.")] [SerializeField] private bool observeChangesFromDisabledState = true; /// /// Whether to observe data changes that were made when the component was disabled and subsequently re-enabled. Events are not raised when component is disabled. /// public bool ObserveChangesFromDisabledState { get { return observeChangesFromDisabledState; } set { observeChangesFromDisabledState = value; } } /// /// The previous value of the . /// protected TType previousDataValue; /// /// Whether to raise the unmodified event. /// protected bool shouldRaiseUnmodifiedEvent; /// /// Resets the back to its default value. /// public virtual void ResetToDefault() { Data = default; } protected virtual void Awake() { CacheExistingValue(); } protected virtual void OnEnable() { shouldRaiseUnmodifiedEvent = false; if (ObserveChangesFromDisabledState) { CheckForChanges(); } shouldRaiseUnmodifiedEvent = true; } protected virtual void OnDisable() { CacheExistingValue(); } /// /// Determines whether the two given values are equal. /// /// The to compare against. /// The to compare with. /// if the two values are considered equal. protected virtual bool Equals(TType a, TType b) { return EqualityComparer.Default.Equals(a, b); } /// /// Caches the existing value. /// protected virtual void CacheExistingValue() { previousDataValue = Data; } /// /// Checks for changes from the current value to the previous value. /// protected virtual void CheckForChanges() { if (!this.IsValidState()) { return; } if (Equals(Data, previousDataValue)) { if (shouldRaiseUnmodifiedEvent) { Unmodified?.Invoke(Data); } } else { if (Equals(previousDataValue, default) && !Equals(Data, default)) { Defined?.Invoke(Data); } Modified?.Invoke(Data); if (!Equals(previousDataValue, default) && Equals(Data, default)) { Defaulted?.Invoke(Data); } } CacheExistingValue(); } /// /// Called before has been changed. /// protected virtual void OnBeforeDataChange() { CacheExistingValue(); } /// /// Called after has been changed. /// protected virtual void OnAfterDataChange() { CheckForChanges(); } } }