123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334 |
- // Animancer // https://kybernetik.com.au/animancer // Copyright 2018-2024 Kybernetik //
- using System;
- using System.Collections;
- using System.Collections.Generic;
- namespace Animancer
- {
- /// <summary>A dictionary which maps event names to callbacks.</summary>
- /// <remarks>
- /// <strong>Documentation:</strong>
- /// <see href="https://kybernetik.com.au/animancer/docs/manual/events/animancer">
- /// Animancer Events</see>
- /// </remarks>
- /// https://kybernetik.com.au/animancer/api/Animancer/NamedEventDictionary
- public class NamedEventDictionary : IDictionary<StringReference, Action>
- {
- /************************************************************************************************************************/
- private readonly Dictionary<StringReference, Action>
- Dictionary = new();
- /************************************************************************************************************************/
- /// <summary>The number of items in this dictionary.</summary>
- public int Count
- => Dictionary.Count;
- /************************************************************************************************************************/
- #region Access
- /************************************************************************************************************************/
- /// <summary>Accesses a callback in this dictionary.</summary>
- public Action this[StringReference name]
- {
- get => Dictionary[name];
- set
- {
- AssertNotEndEvent(name);
- Dictionary[name] = value;
- }
- }
- /************************************************************************************************************************/
- /// <summary>Returns the callback registered using the `name`.</summary>
- /// <remarks>Returns <c>null</c> if nothing was registered.</remarks>
- public Action Get(StringReference name)
- => Dictionary.Get(name);
- /// <summary>Registers the callback using the `name`, replacing anything previously registered.</summary>
- public void Set(StringReference name, Action callback)
- {
- AssertNotEndEvent(name);
- Dictionary[name] = callback;
- }
- /************************************************************************************************************************/
- /// <summary>Are any callbacks registered for the `name`?</summary>
- /// <remarks>To get the registered callbacks at the same time, use <see cref="TryGetValue"/> instead.</remarks>
- public bool ContainsKey(StringReference name)
- => Dictionary.ContainsKey(name);
- /************************************************************************************************************************/
- /// <summary>Tries to get the `callback` registered with the `name` and returns <c>true</c> if successful.</summary>
- public bool TryGetValue(StringReference name, out Action callback)
- => Dictionary.TryGetValue(name, out callback);
- /************************************************************************************************************************/
- #endregion
- /************************************************************************************************************************/
- #region Add
- /************************************************************************************************************************/
- /// <summary>Adds the `callback` to any existing ones registered with the `name`.</summary>
- /// <remarks>
- /// If you want an exception to be thrown if something is already registered with the `name`,
- /// use <see cref="AddNew(StringReference, Action)"/> instead.
- /// </remarks>
- public void AddTo(StringReference name, Action callback)
- {
- AssertNotEndEvent(name);
- if (Dictionary.TryGetValue(name, out var existing))
- callback = existing + callback;
- Dictionary[name] = callback;
- }
- /************************************************************************************************************************/
- /// <summary>
- /// Registers the `callback` with the `name` but throws an <see cref="ArgumentException"/>
- /// if something was already registered with the same `name`.
- /// </summary>
- /// <remarks>
- /// This matches the standard <see cref="Dictionary{TKey, TValue}.Add(TKey, TValue)"/> behaviour,
- /// unlike <see cref="AddTo(StringReference, Action)"/>.
- /// </remarks>
- public void AddNew(StringReference name, Action callback)
- {
- AssertNotEndEvent(name);
- Dictionary.Add(name, callback);
- }
- void IDictionary<StringReference, Action>.Add(StringReference name, Action callback)
- => AddNew(name, callback);
- /************************************************************************************************************************/
- /// <summary>
- /// Adds the `callback` to any existing ones registered with the `name`.
- /// <para></para>
- /// It will be invoked using <see cref="AnimancerEvent.GetCurrentParameter{T}"/> to get its parameter.
- /// </summary>
- /// <remarks>
- /// If you want an exception to be thrown if something is already registered with the `name`,
- /// use <see cref="AddNew{T}(StringReference, Action{T})"/> instead.
- /// <para></para>
- /// If <typeparamref name="T"/> is <see cref="string"/>,
- /// consider using <see cref="AddTo(StringReference, Action{string})"/> instead of this overload.
- /// <para></para>
- /// If you want to later remove the `callback`,
- /// you need to store and remove the returned <see cref="Action"/>.
- /// </remarks>
- public Action AddTo<T>(StringReference name, Action<T> callback)
- {
- AssertNotEndEvent(name);
- var parametized = AnimancerEvent.Parametize(callback);
- if (Dictionary.TryGetValue(name, out var existing))
- parametized = existing + parametized;
- Dictionary[name] = parametized;
- return parametized;
- }
- /// <summary>
- /// Registers the `callback` with the `name` but throws an <see cref="ArgumentException"/>
- /// if something was already registered with the same `name`.
- /// <para></para>
- /// It will be invoked using <see cref="AnimancerEvent.GetCurrentParameter{T}"/> to get its parameter.
- /// </summary>
- /// <remarks>
- /// This matches the standard <see cref="Dictionary{TKey, TValue}.Add(TKey, TValue)"/> behaviour,
- /// unlike <see cref="AddTo{T}(StringReference, Action{T})"/>.
- /// If <typeparamref name="T"/> is <see cref="string"/>,
- /// consider using <see cref="AddTo(StringReference, Action{string})"/> instead of this overload.
- /// <para></para>
- /// If you want to later remove the `callback`,
- /// you need to store and remove the returned <see cref="Action"/>.
- /// </remarks>
- public Action AddNew<T>(StringReference name, Action<T> callback)
- {
- AssertNotEndEvent(name);
- var parametized = AnimancerEvent.Parametize(callback);
- Dictionary.Add(name, parametized);
- return parametized;
- }
- /************************************************************************************************************************/
- /// <summary>
- /// Adds the `callback` to any existing ones registered with the `name`.
- /// <para></para>
- /// It will be invoked using <see cref="object.ToString"/> on the
- /// <see cref="AnimancerEvent.CurrentParameter"/>.
- /// </summary>
- /// <remarks>
- /// If you want an exception to be thrown if something is already registered with the `name`,
- /// use <see cref="AddNew{T}(StringReference, Action{T})"/> instead.
- /// <para></para>
- /// If you want to later remove the `callback`,
- /// you need to store and remove the returned <see cref="Action"/>.
- /// </remarks>
- public Action AddTo(StringReference name, Action<string> callback)
- {
- AssertNotEndEvent(name);
- var parametized = AnimancerEvent.Parametize(callback);
- if (Dictionary.TryGetValue(name, out var existing))
- parametized = existing + parametized;
- Dictionary[name] = parametized;
- return parametized;
- }
- /// <summary>
- /// Registers the `callback` with the `name` but throws an <see cref="ArgumentException"/>
- /// if something was already registered with the same `name`.
- /// <para></para>
- /// It will be invoked using <see cref="object.ToString"/> on the
- /// <see cref="AnimancerEvent.CurrentParameter"/>.
- /// </summary>
- /// <remarks>
- /// This matches the standard <see cref="Dictionary{TKey, TValue}.Add(TKey, TValue)"/>
- /// behaviour, unlike <see cref="AddTo(StringReference, Action{string})"/>.
- /// <para></para>
- /// If you want to later remove the `callback`,
- /// you need to store and remove the returned <see cref="Action"/>.
- /// </remarks>
- public Action AddNew(StringReference name, Action<string> callback)
- {
- AssertNotEndEvent(name);
- var parametized = AnimancerEvent.Parametize(callback);
- Dictionary.Add(name, parametized);
- return parametized;
- }
- /************************************************************************************************************************/
- /// <summary>[Assert-Conditional]
- /// Throws an <see cref="ArgumentException"/> if the `name` is the <see cref="AnimancerEvent.EndEventName"/>.
- /// </summary>
- /// <remarks>
- /// In order to minimise the performance cost of End Events when there isn't one,
- /// the <see cref="AnimancerEvent.Dispatcher"/> won't even check the end time
- /// when there is no <see cref="AnimancerEvent.Sequence.OnEnd"/> callback.
- /// <para></para>
- /// That means if a callback was bound to the <see cref="AnimancerEvent.EndEventName"/>
- /// it would be triggered by any state with an <see cref="AnimancerEvent.Sequence.OnEnd"/>
- /// callback, but not by states without one. That would be very counterintuitive so it isn't allowed.
- /// </remarks>
- /// <exception cref="ArgumentException"/>
- [System.Diagnostics.Conditional(Strings.Assertions)]
- public static void AssertNotEndEvent(StringReference name)
- {
- if (name == AnimancerEvent.EndEventName)
- throw new ArgumentException(
- $"Binding event callbacks to the " +
- $"{nameof(AnimancerEvent)}.{nameof(AnimancerEvent.EndEventName)}" +
- $" is not supported for performance optimization reasons. See the documentation of" +
- $" {nameof(NamedEventDictionary)}.{nameof(AssertNotEndEvent)} for more details.",
- nameof(name));
- }
- /************************************************************************************************************************/
- #endregion
- /************************************************************************************************************************/
- #region Remove
- /************************************************************************************************************************/
- /// <summary>Removes all callbacks registered with the `name`.</summary>
- public bool Remove(StringReference name)
- => Dictionary.Remove(name);
- /// <summary>Removes a specific `callback` registered with the `name`.</summary>
- public bool Remove(StringReference name, Action callback)
- {
- if (!Dictionary.TryGetValue(name, out var callbacks))
- return false;
- if (callbacks == callback)
- Dictionary.Remove(name);
- else
- Dictionary[name] = callbacks - callback;
- return true;
- }
- /************************************************************************************************************************/
- /// <summary>Removes everything from this dictionary.</summary>
- public void Clear()
- => Dictionary.Clear();
- /************************************************************************************************************************/
- #endregion
- /************************************************************************************************************************/
- #region Enumeration
- /************************************************************************************************************************/
- /// <summary>Returns an enumerator to go through every item in this dictionary.</summary>
- public Dictionary<StringReference, Action>.Enumerator GetEnumerator()
- => Dictionary.GetEnumerator();
- IEnumerator<KeyValuePair<StringReference, Action>>
- IEnumerable<KeyValuePair<StringReference, Action>>.GetEnumerator()
- => Dictionary.GetEnumerator();
- IEnumerator IEnumerable.GetEnumerator()
- => Dictionary.GetEnumerator();
- /************************************************************************************************************************/
- /// <summary>The names in this dictionary.</summary>
- public Dictionary<StringReference, Action>.KeyCollection Keys
- => Dictionary.Keys;
- /// <summary>The values in this dictionary.</summary>
- public Dictionary<StringReference, Action>.ValueCollection Values
- => Dictionary.Values;
- ICollection<StringReference> IDictionary<StringReference, Action>.Keys
- => Dictionary.Keys;
- ICollection<Action> IDictionary<StringReference, Action>.Values
- => Dictionary.Values;
- /************************************************************************************************************************/
- #endregion
- /************************************************************************************************************************/
- #region Explicit Dictionary Wrappers
- /************************************************************************************************************************/
- void ICollection<KeyValuePair<StringReference, Action>>
- .Add(KeyValuePair<StringReference, Action> item)
- => AddTo(item.Key, item.Value);
- bool ICollection<KeyValuePair<StringReference, Action>>
- .Contains(KeyValuePair<StringReference, Action> item)
- => ((ICollection<KeyValuePair<StringReference, Action>>)Dictionary).Contains(item);
- void ICollection<KeyValuePair<StringReference, Action>>
- .CopyTo(KeyValuePair<StringReference, Action>[] array,
- int arrayIndex)
- => ((ICollection<KeyValuePair<StringReference, Action>>)Dictionary).CopyTo(array, arrayIndex);
- bool ICollection<KeyValuePair<StringReference, Action>>
- .Remove(KeyValuePair<StringReference, Action> item)
- => Dictionary.Remove(item.Key);
- bool ICollection<KeyValuePair<StringReference, Action>>.IsReadOnly
- => false;
- /************************************************************************************************************************/
- #endregion
- /************************************************************************************************************************/
- }
- }
|