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