feat: added priorities to events

This commit is contained in:
Syntriax 2025-07-06 22:22:57 +03:00
parent 8f03628bd6
commit bc1c76d746

View File

@ -45,30 +45,71 @@ namespace Syntriax.Engine.Core;
/// </summary> /// </summary>
public class Event public class Event
{ {
private readonly List<EventHandler> listeners = null!; // We use Ascending order because draw calls are running from last to first
private readonly List<EventHandler> onceListeners = null!; private static readonly Comparer<ListenerData> SortByAscendingPriority = Comparer<ListenerData>.Create((x, y) => x.Priority.CompareTo(y.Priority));
private readonly List<ListenerData> listeners = null!;
private readonly List<ListenerData> onceListeners = null!;
/// <summary> /// <summary>
/// Subscribes the callback to be invoked whenever the event is triggered. /// Subscribes the callback to be invoked whenever the event is triggered.
/// </summary> /// </summary>
/// <param name="listener">The callback to be called when the event is triggered.</param> /// <param name="listener">The callback to be called when the event is triggered.</param>
public void AddListener(EventHandler listener) => listeners.Add(listener); /// <param name="priority">Priority of the callback.</param>
public void AddListener(EventHandler listener, int priority = 0)
{
ListenerData listenerData = new(listener, priority);
int insertIndex = listeners.BinarySearch(listenerData, SortByAscendingPriority);
if (insertIndex < 0)
insertIndex = ~insertIndex;
listeners.Insert(insertIndex, listenerData);
}
/// <summary> /// <summary>
/// Subscribes the callback to be invoked the next time the event is triggered. The callback will be called only once. /// Subscribes the callback to be invoked the next time the event is triggered. The callback will be called only once.
/// </summary> /// </summary>
/// <param name="listener">The callback to be called the next time the event is triggered.</param> /// <param name="listener">The callback to be called the next time the event is triggered.</param>
public void AddOneTimeListener(EventHandler listener) => onceListeners.Add(listener); /// <param name="priority">Priority of the callback.</param>
public void AddOneTimeListener(EventHandler listener, int priority = 0)
{
ListenerData listenerData = new(listener, priority);
int insertIndex = onceListeners.BinarySearch(listenerData, SortByAscendingPriority);
if (insertIndex < 0)
insertIndex = ~insertIndex;
onceListeners.Insert(insertIndex, listenerData);
}
/// <summary> /// <summary>
/// Unsubscribes the callback that was previously registered by <see cref="AddListener(EventHandler)"/>. /// Unsubscribes the callback that was previously registered by <see cref="AddListener(EventHandler)"/>.
/// </summary> /// </summary>
/// <param name="listener">The callback that was previously registered by <see cref="AddListener(EventHandler)"/></param> /// <param name="listener">The callback that was previously registered by <see cref="AddListener(EventHandler)"/></param>
public void RemoveListener(EventHandler listener) => listeners.Remove(listener); public void RemoveListener(EventHandler listener)
{
for (int i = listeners.Count - 1; i >= 0; i--)
if (listeners[i].Callback == listener)
{
listeners.RemoveAt(i);
return;
}
}
/// <summary> /// <summary>
/// Unsubscribes the callback that was previously registered by <see cref="AddOneTimeListener(EventHandler)"/>. /// Unsubscribes the callback that was previously registered by <see cref="AddOneTimeListener(EventHandler)"/>.
/// </summary> /// </summary>
/// <param name="listener">The callback that was previously registered by <see cref="AddOneTimeListener(EventHandler)"/></param> /// <param name="listener">The callback that was previously registered by <see cref="AddOneTimeListener(EventHandler)"/></param>
public void RemoveOneTimeListener(EventHandler listener) => onceListeners.Remove(listener); public void RemoveOneTimeListener(EventHandler listener)
{
for (int i = 0; i < onceListeners.Count; i++)
if (onceListeners[i].Callback == listener)
{
onceListeners.RemoveAt(i);
return;
}
}
/// <summary> /// <summary>
/// Unsubscribes all listeners that was previously registered by either <see cref="AddListener(EventHandler)"/> or <see cref="AddOneTimeListener(EventHandler)"/>. /// Unsubscribes all listeners that was previously registered by either <see cref="AddListener(EventHandler)"/> or <see cref="AddOneTimeListener(EventHandler)"/>.
@ -81,19 +122,19 @@ public class Event
public void Invoke() public void Invoke()
{ {
for (int i = listeners.Count - 1; i >= 0; i--) for (int i = listeners.Count - 1; i >= 0; i--)
try { listeners[i].Invoke(); } try { listeners[i].Callback.Invoke(); }
catch (Exception exception) catch (Exception exception)
{ {
string methodCallRepresentation = $"{listeners[i].Method.DeclaringType?.FullName}.{listeners[i].Method.Name}()"; string methodCallRepresentation = $"{listeners[i].Callback.Method.DeclaringType?.FullName}.{listeners[i].Callback.Method.Name}()";
Console.WriteLine($"Unexpected exception on invocation of method {methodCallRepresentation}:{Environment.NewLine}{exception.InnerException}"); Console.WriteLine($"Unexpected exception on invocation of method {methodCallRepresentation}:{Environment.NewLine}{exception.InnerException}");
} }
for (int i = onceListeners.Count - 1; i >= 0; i--) for (int i = onceListeners.Count - 1; i >= 0; i--)
{ {
try { onceListeners[i].Invoke(); } try { onceListeners[i].Callback.Invoke(); }
catch (Exception exception) catch (Exception exception)
{ {
string methodCallRepresentation = $"{onceListeners[i].Method.DeclaringType?.FullName}.{onceListeners[i].Method.Name}()"; string methodCallRepresentation = $"{onceListeners[i].Callback.Method.DeclaringType?.FullName}.{onceListeners[i].Callback.Method.Name}()";
Console.WriteLine($"Unexpected exception on invocation of method {methodCallRepresentation}:{Environment.NewLine}{exception.InnerException}"); Console.WriteLine($"Unexpected exception on invocation of method {methodCallRepresentation}:{Environment.NewLine}{exception.InnerException}");
} }
onceListeners.RemoveAt(i); onceListeners.RemoveAt(i);
@ -113,6 +154,7 @@ public class Event
} }
public delegate void EventHandler(); public delegate void EventHandler();
private record struct ListenerData(EventHandler Callback, int Priority);
} }
/// <summary> /// <summary>
@ -159,30 +201,71 @@ public class Event
/// <typeparam name="TSender">Sender type</typeparam> /// <typeparam name="TSender">Sender type</typeparam>
public class Event<TSender> public class Event<TSender>
{ {
private readonly List<EventHandler> listeners = null!; // We use Ascending order because draw calls are running from last to first
private readonly List<EventHandler> onceListeners = null!; private static readonly Comparer<ListenerData> SortByAscendingPriority = Comparer<ListenerData>.Create((x, y) => x.Priority.CompareTo(y.Priority));
private readonly List<ListenerData> listeners = null!;
private readonly List<ListenerData> onceListeners = null!;
/// <summary> /// <summary>
/// Subscribes the callback to be invoked whenever the event is triggered. /// Subscribes the callback to be invoked whenever the event is triggered.
/// </summary> /// </summary>
/// <param name="listener">The callback to be called when the event is triggered.</param> /// <param name="listener">The callback to be called when the event is triggered.</param>
public void AddListener(EventHandler listener) => listeners.Add(listener); /// <param name="priority">Priority of the callback.</param>
public void AddListener(EventHandler listener, int priority = 0)
{
ListenerData listenerData = new(listener, priority);
int insertIndex = listeners.BinarySearch(listenerData, SortByAscendingPriority);
if (insertIndex < 0)
insertIndex = ~insertIndex;
listeners.Insert(insertIndex, listenerData);
}
/// <summary> /// <summary>
/// Subscribes the callback to be invoked the next time the event is triggered. The callback will be called only once. /// Subscribes the callback to be invoked the next time the event is triggered. The callback will be called only once.
/// </summary> /// </summary>
/// <param name="listener">The callback to be called the next time the event is triggered.</param> /// <param name="listener">The callback to be called the next time the event is triggered.</param>
public void AddOneTimeListener(EventHandler listener) => onceListeners.Add(listener); /// <param name="priority">Priority of the callback.</param>
public void AddOneTimeListener(EventHandler listener, int priority = 0)
{
ListenerData listenerData = new(listener, priority);
int insertIndex = onceListeners.BinarySearch(listenerData, SortByAscendingPriority);
if (insertIndex < 0)
insertIndex = ~insertIndex;
onceListeners.Insert(insertIndex, listenerData);
}
/// <summary> /// <summary>
/// Unsubscribes the callback that was previously registered by <see cref="AddListener(EventHandler)"/>. /// Unsubscribes the callback that was previously registered by <see cref="AddListener(EventHandler)"/>.
/// </summary> /// </summary>
/// <param name="listener">The callback that was previously registered by <see cref="AddListener(EventHandler)"/></param> /// <param name="listener">The callback that was previously registered by <see cref="AddListener(EventHandler)"/></param>
public void RemoveListener(EventHandler listener) => listeners.Remove(listener); public void RemoveListener(EventHandler listener)
{
for (int i = listeners.Count - 1; i >= 0; i--)
if (listeners[i].Callback == listener)
{
listeners.RemoveAt(i);
return;
}
}
/// <summary> /// <summary>
/// Unsubscribes the callback that was previously registered by <see cref="AddOneTimeListener(EventHandler)"/>. /// Unsubscribes the callback that was previously registered by <see cref="AddOneTimeListener(EventHandler)"/>.
/// </summary> /// </summary>
/// <param name="listener">The callback that was previously registered by <see cref="AddOneTimeListener(EventHandler)"/></param> /// <param name="listener">The callback that was previously registered by <see cref="AddOneTimeListener(EventHandler)"/></param>
public void RemoveOneTimeListener(EventHandler listener) => onceListeners.Remove(listener); public void RemoveOneTimeListener(EventHandler listener)
{
for (int i = 0; i < onceListeners.Count; i++)
if (onceListeners[i].Callback == listener)
{
onceListeners.RemoveAt(i);
return;
}
}
/// <summary> /// <summary>
/// Unsubscribes all listeners that was previously registered by either <see cref="AddListener(EventHandler)"/> or <see cref="AddOneTimeListener(EventHandler)"/>. /// Unsubscribes all listeners that was previously registered by either <see cref="AddListener(EventHandler)"/> or <see cref="AddOneTimeListener(EventHandler)"/>.
@ -196,19 +279,19 @@ public class Event<TSender>
public void Invoke(TSender sender) public void Invoke(TSender sender)
{ {
for (int i = listeners.Count - 1; i >= 0; i--) for (int i = listeners.Count - 1; i >= 0; i--)
try { listeners[i].Invoke(sender); } try { listeners[i].Callback.Invoke(sender); }
catch (Exception exception) catch (Exception exception)
{ {
string methodCallRepresentation = $"{listeners[i].Method.DeclaringType?.FullName}.{listeners[i].Method.Name}({sender})"; string methodCallRepresentation = $"{listeners[i].Callback.Method.DeclaringType?.FullName}.{listeners[i].Callback.Method.Name}({sender})";
Console.WriteLine($"Unexpected exception on invocation of method {methodCallRepresentation}:{Environment.NewLine}{exception.InnerException}"); Console.WriteLine($"Unexpected exception on invocation of method {methodCallRepresentation}:{Environment.NewLine}{exception.InnerException}");
} }
for (int i = onceListeners.Count - 1; i >= 0; i--) for (int i = onceListeners.Count - 1; i >= 0; i--)
{ {
try { onceListeners[i].Invoke(sender); } try { onceListeners[i].Callback.Invoke(sender); }
catch (Exception exception) catch (Exception exception)
{ {
string methodCallRepresentation = $"{onceListeners[i].Method.DeclaringType?.FullName}.{onceListeners[i].Method.Name}({sender})"; string methodCallRepresentation = $"{onceListeners[i].Callback.Method.DeclaringType?.FullName}.{onceListeners[i].Callback.Method.Name}({sender})";
Console.WriteLine($"Unexpected exception on invocation of method {methodCallRepresentation}:{Environment.NewLine}{exception.InnerException}"); Console.WriteLine($"Unexpected exception on invocation of method {methodCallRepresentation}:{Environment.NewLine}{exception.InnerException}");
} }
onceListeners.RemoveAt(i); onceListeners.RemoveAt(i);
@ -228,6 +311,7 @@ public class Event<TSender>
} }
public delegate void EventHandler(TSender sender); public delegate void EventHandler(TSender sender);
private record struct ListenerData(EventHandler Callback, int Priority);
} }
/// <summary> /// <summary>
@ -281,30 +365,71 @@ public class Event<TSender>
/// <typeparam name="TSender">Sender type</typeparam> /// <typeparam name="TSender">Sender type</typeparam>
public class Event<TSender, TArguments> public class Event<TSender, TArguments>
{ {
private readonly List<EventHandler> listeners = null!; // We use Ascending order because draw calls are running from last to first
private readonly List<EventHandler> onceListeners = null!; private static readonly Comparer<ListenerData> SortByAscendingPriority = Comparer<ListenerData>.Create((x, y) => x.Priority.CompareTo(y.Priority));
private readonly List<ListenerData> listeners = null!;
private readonly List<ListenerData> onceListeners = null!;
/// <summary> /// <summary>
/// Subscribes the callback to be invoked whenever the event is triggered. /// Subscribes the callback to be invoked whenever the event is triggered.
/// </summary> /// </summary>
/// <param name="listener">The callback to be called when the event is triggered.</param> /// <param name="listener">The callback to be called when the event is triggered.</param>
public void AddListener(EventHandler listener) => listeners.Add(listener); /// <param name="priority">Priority of the callback.</param>
public void AddListener(EventHandler listener, int priority = 0)
{
ListenerData listenerData = new(listener, priority);
int insertIndex = listeners.BinarySearch(listenerData, SortByAscendingPriority);
if (insertIndex < 0)
insertIndex = ~insertIndex;
listeners.Insert(insertIndex, listenerData);
}
/// <summary> /// <summary>
/// Subscribes the callback to be invoked the next time the event is triggered. The callback will be called only once. /// Subscribes the callback to be invoked the next time the event is triggered. The callback will be called only once.
/// </summary> /// </summary>
/// <param name="listener">The callback to be called the next time the event is triggered.</param> /// <param name="listener">The callback to be called the next time the event is triggered.</param>
public void AddOneTimeListener(EventHandler listener) => onceListeners.Add(listener); /// <param name="priority">Priority of the callback.</param>
public void AddOneTimeListener(EventHandler listener, int priority = 0)
{
ListenerData listenerData = new(listener, priority);
int insertIndex = onceListeners.BinarySearch(listenerData, SortByAscendingPriority);
if (insertIndex < 0)
insertIndex = ~insertIndex;
onceListeners.Insert(insertIndex, listenerData);
}
/// <summary> /// <summary>
/// Unsubscribes the callback that was previously registered by <see cref="AddListener(EventHandler)"/>. /// Unsubscribes the callback that was previously registered by <see cref="AddListener(EventHandler)"/>.
/// </summary> /// </summary>
/// <param name="listener">The callback that was previously registered by <see cref="AddListener(EventHandler)"/></param> /// <param name="listener">The callback that was previously registered by <see cref="AddListener(EventHandler)"/></param>
public void RemoveListener(EventHandler listener) => listeners.Remove(listener); public void RemoveListener(EventHandler listener)
{
for (int i = listeners.Count - 1; i >= 0; i--)
if (listeners[i].Callback == listener)
{
listeners.RemoveAt(i);
return;
}
}
/// <summary> /// <summary>
/// Unsubscribes the callback that was previously registered by <see cref="AddOneTimeListener(EventHandler)"/>. /// Unsubscribes the callback that was previously registered by <see cref="AddOneTimeListener(EventHandler)"/>.
/// </summary> /// </summary>
/// <param name="listener">The callback that was previously registered by <see cref="AddOneTimeListener(EventHandler)"/></param> /// <param name="listener">The callback that was previously registered by <see cref="AddOneTimeListener(EventHandler)"/></param>
public void RemoveOneTimeListener(EventHandler listener) => onceListeners.Remove(listener); public void RemoveOneTimeListener(EventHandler listener)
{
for (int i = 0; i < onceListeners.Count; i++)
if (onceListeners[i].Callback == listener)
{
onceListeners.RemoveAt(i);
return;
}
}
/// <summary> /// <summary>
/// Unsubscribes all listeners that was previously registered by either <see cref="AddListener(EventHandler)"/> or <see cref="AddOneTimeListener(EventHandler)"/>. /// Unsubscribes all listeners that was previously registered by either <see cref="AddListener(EventHandler)"/> or <see cref="AddOneTimeListener(EventHandler)"/>.
@ -319,19 +444,19 @@ public class Event<TSender, TArguments>
public void Invoke(TSender sender, TArguments args) public void Invoke(TSender sender, TArguments args)
{ {
for (int i = listeners.Count - 1; i >= 0; i--) for (int i = listeners.Count - 1; i >= 0; i--)
try { listeners[i].Invoke(sender, args); } try { listeners[i].Callback.Invoke(sender, args); }
catch (Exception exception) catch (Exception exception)
{ {
string methodCallRepresentation = $"{listeners[i].Method.DeclaringType?.FullName}.{listeners[i].Method.Name}({string.Join(", ", sender, args)})"; string methodCallRepresentation = $"{listeners[i].Callback.Method.DeclaringType?.FullName}.{listeners[i].Callback.Method.Name}({string.Join(", ", sender, args)})";
Console.WriteLine($"Unexpected exception on invocation of method {methodCallRepresentation}:{Environment.NewLine}{exception.InnerException}"); Console.WriteLine($"Unexpected exception on invocation of method {methodCallRepresentation}:{Environment.NewLine}{exception.InnerException}");
} }
for (int i = onceListeners.Count - 1; i >= 0; i--) for (int i = onceListeners.Count - 1; i >= 0; i--)
{ {
try { onceListeners[i].Invoke(sender, args); } try { onceListeners[i].Callback.Invoke(sender, args); }
catch (Exception exception) catch (Exception exception)
{ {
string methodCallRepresentation = $"{onceListeners[i].Method.DeclaringType?.FullName}.{onceListeners[i].Method.Name}({string.Join(", ", sender, args)})"; string methodCallRepresentation = $"{onceListeners[i].Callback.Method.DeclaringType?.FullName}.{onceListeners[i].Callback.Method.Name}({string.Join(", ", sender, args)})";
Console.WriteLine($"Unexpected exception on invocation of method {methodCallRepresentation}:{Environment.NewLine}{exception.InnerException}"); Console.WriteLine($"Unexpected exception on invocation of method {methodCallRepresentation}:{Environment.NewLine}{exception.InnerException}");
} }
onceListeners.RemoveAt(i); onceListeners.RemoveAt(i);
@ -351,4 +476,5 @@ public class Event<TSender, TArguments>
} }
public delegate void EventHandler(TSender sender, TArguments args); public delegate void EventHandler(TSender sender, TArguments args);
private record struct ListenerData(EventHandler Callback, int Priority);
} }