Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ public void Constructor_CreatesEmptyDictionary()
public void Add_AddsItemAndTriggersEvent()
{
var dict = new TrackingDictionary<string, int>();
TrackingCollectionChangedEventArgs? eventArgs = null;
TrackingKeyedCollectionChangedEventArgs<string, int>? eventArgs = null;
dict.CollectionChanged += (sender, args) => eventArgs = args;

dict.Add("key1", 10);
Expand All @@ -34,7 +34,7 @@ public void Add_AddsItemAndTriggersEvent()
public void Remove_RemovesItemAndTriggersEvent()
{
var dict = new TrackingDictionary<string, int> { { "key1", 10 } };
TrackingCollectionChangedEventArgs? eventArgs = null;
TrackingKeyedCollectionChangedEventArgs<string, int>? eventArgs = null;
dict.CollectionChanged += (sender, args) =>
{
if (args.Action == NotifyCollectionChangedAction.Remove)
Expand Down Expand Up @@ -218,7 +218,7 @@ public void CollectionChanged_CanBeUnsubscribed()
{
var dict = new TrackingDictionary<string, int>();
var eventCount = 0;
EventHandler<TrackingCollectionChangedEventArgs> handler = (s, e) => eventCount++;
EventHandler<TrackingKeyedCollectionChangedEventArgs<string, int>> handler = (s, e) => eventCount++;

dict.CollectionChanged += handler;
dict.Add("key1", 10);
Expand Down
Original file line number Diff line number Diff line change
@@ -1,13 +1,18 @@
// Copyright (c) .NET Foundation and Contributors (https://dotnetfoundation.org/ & https://stride3d.net) and Silicon Studio Corp. (https://www.siliconstudio.co.jp)
// Distributed under the MIT license. See the LICENSE.md file in the project root for more information.

namespace Stride.Core.Collections;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

public interface ITrackingCollectionChanged
namespace Stride.Core.Collections;
public interface ITrackingCollectionChanged<TValue>
{
/// <summary>
/// Occurs when [collection changed].
/// </summary>
/// Called as is when adding an item, and in reverse-order when removing an item.
event EventHandler<TrackingCollectionChangedEventArgs> CollectionChanged;
event EventHandler<TrackingCollectionChangedEventArgs<TValue>> CollectionChanged;
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
// Copyright (c) .NET Foundation and Contributors (https://dotnetfoundation.org/ & https://stride3d.net) and Silicon Studio Corp. (https://www.siliconstudio.co.jp)
// Distributed under the MIT license. See the LICENSE.md file in the project root for more information.

namespace Stride.Core.Collections;

public interface ITrackingKeyedCollectionChanged<TKey, TValue>
{
/// <summary>
/// Occurs when [collection changed].
/// </summary>
/// Called as is when adding an item, and in reverse-order when removing an item.
event EventHandler<TrackingKeyedCollectionChangedEventArgs<TKey, TValue>> CollectionChanged;
}
28 changes: 14 additions & 14 deletions sources/core/Stride.Core/Collections/TrackingCollection.cs
Original file line number Diff line number Diff line change
Expand Up @@ -12,38 +12,38 @@ namespace Stride.Core.Collections;
/// </summary>
/// <typeparam name="T">The type of elements in the collection.</typeparam>
[DataSerializer(typeof(ListAllSerializer<,>), Mode = DataSerializerGenericMode.TypeAndGenericArguments)]
public class TrackingCollection<T> : FastCollection<T>, ITrackingCollectionChanged
public class TrackingCollection<T> : FastCollection<T>, ITrackingCollectionChanged<T>
{
private EventHandler<TrackingCollectionChangedEventArgs>? itemAdded;
private EventHandler<TrackingCollectionChangedEventArgs>? itemRemoved;
private EventHandler<TrackingCollectionChangedEventArgs<T>>? itemAdded;
private EventHandler<TrackingCollectionChangedEventArgs<T>>? itemRemoved;

/// <inheritdoc/>
public event EventHandler<TrackingCollectionChangedEventArgs> CollectionChanged
public event EventHandler<TrackingCollectionChangedEventArgs<T>> CollectionChanged
{
add
{
// We keep a list in reverse order for removal, so that we can easily have multiple handlers depending on each others
itemAdded = (EventHandler<TrackingCollectionChangedEventArgs>)Delegate.Combine(itemAdded, value);
itemRemoved = (EventHandler<TrackingCollectionChangedEventArgs>)Delegate.Combine(value, itemRemoved);
itemAdded = (EventHandler<TrackingCollectionChangedEventArgs<T>>)Delegate.Combine(itemAdded, value);
itemRemoved = (EventHandler<TrackingCollectionChangedEventArgs<T>>)Delegate.Combine(value, itemRemoved);
}
remove
{
itemAdded = (EventHandler<TrackingCollectionChangedEventArgs>?)Delegate.Remove(itemAdded, value);
itemRemoved = (EventHandler<TrackingCollectionChangedEventArgs>?)Delegate.Remove(itemRemoved, value);
itemAdded = (EventHandler<TrackingCollectionChangedEventArgs<T>>?)Delegate.Remove(itemAdded, value);
itemRemoved = (EventHandler<TrackingCollectionChangedEventArgs<T>>?)Delegate.Remove(itemRemoved, value);
}
}

/// <inheritdoc/>
protected override void InsertItem(int index, T item)
{
base.InsertItem(index, item);
itemAdded?.Invoke(this, new TrackingCollectionChangedEventArgs(NotifyCollectionChangedAction.Add, item, null, index, true));
itemAdded?.Invoke(this, new TrackingCollectionChangedEventArgs<T>(NotifyCollectionChangedAction.Add, item, default, index, true));
}

/// <inheritdoc/>
protected override void RemoveItem(int index)
{
itemRemoved?.Invoke(this, new TrackingCollectionChangedEventArgs(NotifyCollectionChangedAction.Remove, this[index], null, index, true));
itemRemoved?.Invoke(this, new TrackingCollectionChangedEventArgs<T>(NotifyCollectionChangedAction.Remove, this[index], default, index, true));
base.RemoveItem(index);
}

Expand All @@ -61,7 +61,7 @@ protected void ClearItemsEvents()
if (collectionChanged != null)
{
for (var i = Count - 1; i >= 0; --i)
collectionChanged(this, new TrackingCollectionChangedEventArgs(NotifyCollectionChangedAction.Remove, this[i], null, i, true));
collectionChanged(this, new TrackingCollectionChangedEventArgs<T>(NotifyCollectionChangedAction.Remove, this[i], default, i, true));
}
}

Expand All @@ -71,11 +71,11 @@ protected override void SetItem(int index, T item)
// Note: Changing CollectionChanged is not thread-safe
var collectionChangedRemoved = itemRemoved;

object? oldItem = collectionChangedRemoved != null ? this[index] : null;
collectionChangedRemoved?.Invoke(this, new TrackingCollectionChangedEventArgs(NotifyCollectionChangedAction.Remove, oldItem, null, index, false));
T? oldItem = collectionChangedRemoved != null ? this[index] : default;
collectionChangedRemoved?.Invoke(this, new TrackingCollectionChangedEventArgs<T>(NotifyCollectionChangedAction.Remove, oldItem, default, index, false));

base.SetItem(index, item);

itemAdded?.Invoke(this, new TrackingCollectionChangedEventArgs(NotifyCollectionChangedAction.Add, item, oldItem, index, false));
itemAdded?.Invoke(this, new TrackingCollectionChangedEventArgs<T>(NotifyCollectionChangedAction.Add, item, oldItem, index, false));
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -4,29 +4,17 @@
using System.Collections.Specialized;

namespace Stride.Core.Collections;

public class TrackingCollectionChangedEventArgs : EventArgs
public sealed class TrackingCollectionChangedEventArgs<TValue> : EventArgs
{
public TrackingCollectionChangedEventArgs(NotifyCollectionChangedAction action, object? item, object? oldItem, int index, bool collectionChanged)
public TrackingCollectionChangedEventArgs(NotifyCollectionChangedAction action, TValue? item, TValue? oldItem, int index, bool collectionChanged)
{
Action = action;
Item = item;
OldItem = oldItem;
Key = null;
Index = index;
CollectionChanged = collectionChanged;
}

public TrackingCollectionChangedEventArgs(NotifyCollectionChangedAction action, object key, object? item, object? oldItem, bool collectionChanged)
{
Action = action;
Item = item;
OldItem = oldItem;
Key = key;
Index = -1;
CollectionChanged = collectionChanged;
}

/// <summary>
/// Gets the type of action performed.
/// Allowed values are <see cref="NotifyCollectionChangedAction.Add"/> and <see cref="NotifyCollectionChangedAction.Remove"/>.
Expand All @@ -36,15 +24,12 @@ public TrackingCollectionChangedEventArgs(NotifyCollectionChangedAction action,
/// <summary>
/// Gets the added or removed item (if dictionary, value only).
/// </summary>
public object? Item { get; }
public TValue? Item { get; }

/// <summary>
/// Gets the previous value. Only valid if <see cref="Action"/> is <see cref="NotifyCollectionChangedAction.Add"/> and <see cref="NotifyCollectionChangedAction.Remove"/>
/// </summary>
public object? OldItem { get; }

/// <summary>Gets the added or removed key (if dictionary).</summary>
public object? Key { get; }
public TValue? OldItem { get; }

/// <summary>
/// Gets the index in the collection (if applicable).
Expand Down
24 changes: 12 additions & 12 deletions sources/core/Stride.Core/Collections/TrackingDictionary.cs
Original file line number Diff line number Diff line change
Expand Up @@ -18,27 +18,27 @@ namespace Stride.Core.Collections;
/// <typeparam name="TKey">The type of the key.</typeparam>
/// <typeparam name="TValue">The type of the value.</typeparam>
[DataSerializer(typeof(DictionaryAllSerializer<,,>), Mode = DataSerializerGenericMode.TypeAndGenericArguments)]
public class TrackingDictionary<TKey, TValue> : IDictionary<TKey, TValue>, IDictionary, ITrackingCollectionChanged
public class TrackingDictionary<TKey, TValue> : IDictionary<TKey, TValue>, IDictionary, ITrackingKeyedCollectionChanged<TKey, TValue>
where TKey : notnull
{
private readonly Dictionary<TKey, TValue> innerDictionary;

private EventHandler<TrackingCollectionChangedEventArgs>? itemAdded;
private EventHandler<TrackingCollectionChangedEventArgs>? itemRemoved;
private EventHandler<TrackingKeyedCollectionChangedEventArgs<TKey, TValue>>? itemAdded;
private EventHandler<TrackingKeyedCollectionChangedEventArgs<TKey, TValue>>? itemRemoved;

/// <inheritdoc/>
public event EventHandler<TrackingCollectionChangedEventArgs> CollectionChanged
public event EventHandler<TrackingKeyedCollectionChangedEventArgs<TKey, TValue>> CollectionChanged
{
add
{
// We keep a list in reverse order for removal, so that we can easily have multiple handlers depending on each others
itemAdded = (EventHandler<TrackingCollectionChangedEventArgs>)Delegate.Combine(itemAdded, value);
itemRemoved = (EventHandler<TrackingCollectionChangedEventArgs>)Delegate.Combine(value, itemRemoved);
itemAdded = (EventHandler<TrackingKeyedCollectionChangedEventArgs<TKey, TValue>>)Delegate.Combine(itemAdded, value);
itemRemoved = (EventHandler<TrackingKeyedCollectionChangedEventArgs<TKey, TValue>>)Delegate.Combine(value, itemRemoved);
}
remove
{
itemAdded = (EventHandler<TrackingCollectionChangedEventArgs>?)Delegate.Remove(itemAdded, value);
itemRemoved = (EventHandler<TrackingCollectionChangedEventArgs>?)Delegate.Remove(itemRemoved, value);
itemAdded = (EventHandler<TrackingKeyedCollectionChangedEventArgs<TKey, TValue>>?)Delegate.Remove(itemAdded, value);
itemRemoved = (EventHandler<TrackingKeyedCollectionChangedEventArgs<TKey, TValue>>?)Delegate.Remove(itemRemoved, value);
}
}

Expand All @@ -54,7 +54,7 @@ public TrackingDictionary()
public void Add(TKey key, TValue value)
{
innerDictionary.Add(key, value);
itemAdded?.Invoke(this, new TrackingCollectionChangedEventArgs(NotifyCollectionChangedAction.Add, key, value, null, true));
itemAdded?.Invoke(this, new TrackingKeyedCollectionChangedEventArgs<TKey, TValue>(NotifyCollectionChangedAction.Add, key, value, default, true));
}

/// <inheritdoc/>
Expand All @@ -74,7 +74,7 @@ public bool Remove(TKey key)
{
var collectionChanged = itemRemoved;
if (collectionChanged != null && innerDictionary.TryGetValue(key, out var dictValue))
collectionChanged(this, new TrackingCollectionChangedEventArgs(NotifyCollectionChangedAction.Remove, key, dictValue, null, true));
collectionChanged(this, new TrackingKeyedCollectionChangedEventArgs<TKey, TValue>(NotifyCollectionChangedAction.Remove, key, dictValue, default, true));

return innerDictionary.Remove(key);
}
Expand Down Expand Up @@ -105,12 +105,12 @@ public TValue this[TKey key]
{
var alreadyExisting = innerDictionary.TryGetValue(key, out var oldValue);
if (alreadyExisting)
collectionChangedRemoved(this, new TrackingCollectionChangedEventArgs(NotifyCollectionChangedAction.Remove, key, oldValue, null, false));
collectionChangedRemoved(this, new TrackingKeyedCollectionChangedEventArgs<TKey, TValue>(NotifyCollectionChangedAction.Remove, key, oldValue, default, false));

innerDictionary[key] = value;

// Note: CollectionChanged is considered not thread-safe, so no need to skip if null here, shouldn't happen
itemAdded?.Invoke(this, new TrackingCollectionChangedEventArgs(NotifyCollectionChangedAction.Add, key, innerDictionary[key], oldValue, !alreadyExisting));
itemAdded?.Invoke(this, new TrackingKeyedCollectionChangedEventArgs<TKey, TValue>(NotifyCollectionChangedAction.Add, key, innerDictionary[key], oldValue, !alreadyExisting));
}
else
{
Expand Down
20 changes: 10 additions & 10 deletions sources/core/Stride.Core/Collections/TrackingHashSet.cs
Original file line number Diff line number Diff line change
Expand Up @@ -12,26 +12,26 @@ namespace Stride.Core.Collections;
/// Underlying storage is done with a <see cref="HashSet{T}"/>.
/// </remarks>
/// <typeparam name="T">The type of elements in the hash set.</typeparam>
public class TrackingHashSet<T> : ISet<T>, IReadOnlySet<T>, ITrackingCollectionChanged
public class TrackingHashSet<T> : ISet<T>, IReadOnlySet<T>, ITrackingCollectionChanged<T>
{
private readonly HashSet<T> innerHashSet = [];

private EventHandler<TrackingCollectionChangedEventArgs>? itemAdded;
private EventHandler<TrackingCollectionChangedEventArgs>? itemRemoved;
private EventHandler<TrackingCollectionChangedEventArgs<T>>? itemAdded;
private EventHandler<TrackingCollectionChangedEventArgs<T>>? itemRemoved;

/// <inheritdoc/>
public event EventHandler<TrackingCollectionChangedEventArgs> CollectionChanged
public event EventHandler<TrackingCollectionChangedEventArgs<T>> CollectionChanged
{
add
{
// We keep a list in reverse order for removal, so that we can easily have multiple handlers depending on each others
itemAdded = (EventHandler<TrackingCollectionChangedEventArgs>)Delegate.Combine(itemAdded, value);
itemRemoved = (EventHandler<TrackingCollectionChangedEventArgs>)Delegate.Combine(value, itemRemoved);
itemAdded = (EventHandler<TrackingCollectionChangedEventArgs<T>>)Delegate.Combine(itemAdded, value);
itemRemoved = (EventHandler<TrackingCollectionChangedEventArgs<T>>)Delegate.Combine(value, itemRemoved);
}
remove
{
itemAdded = (EventHandler<TrackingCollectionChangedEventArgs>?)Delegate.Remove(itemAdded, value);
itemRemoved = (EventHandler<TrackingCollectionChangedEventArgs>?)Delegate.Remove(itemRemoved, value);
itemAdded = (EventHandler<TrackingCollectionChangedEventArgs<T>>?)Delegate.Remove(itemAdded, value);
itemRemoved = (EventHandler<TrackingCollectionChangedEventArgs<T>>?)Delegate.Remove(itemRemoved, value);
}
}

Expand All @@ -40,7 +40,7 @@ public bool Add(T item)
{
if (innerHashSet.Add(item))
{
itemAdded?.Invoke(this, new TrackingCollectionChangedEventArgs(NotifyCollectionChangedAction.Add, item, null, -1, true));
itemAdded?.Invoke(this, new TrackingCollectionChangedEventArgs<T>(NotifyCollectionChangedAction.Add, item, default, -1, true));
return true;
}

Expand Down Expand Up @@ -153,7 +153,7 @@ public bool Remove(T item)
if (!innerHashSet.Remove(item))
return false;

itemRemoved?.Invoke(this, new TrackingCollectionChangedEventArgs(NotifyCollectionChangedAction.Remove, item, null, -1, true));
itemRemoved?.Invoke(this, new TrackingCollectionChangedEventArgs<T>(NotifyCollectionChangedAction.Remove, item, default, -1, true));
return true;
}

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
// Copyright (c) .NET Foundation and Contributors (https://dotnetfoundation.org/ & https://stride3d.net) and Silicon Studio Corp. (https://www.siliconstudio.co.jp)
// Distributed under the MIT license. See the LICENSE.md file in the project root for more information.

using System.Collections.Specialized;

namespace Stride.Core.Collections;

public sealed class TrackingKeyedCollectionChangedEventArgs<TKey, TValue> : EventArgs
{
public TrackingKeyedCollectionChangedEventArgs(NotifyCollectionChangedAction action, TKey key, TValue? item, TValue? oldItem, bool collectionChanged)
{
Action = action;
Item = item;
OldItem = oldItem;
Key = key;
Index = -1;
CollectionChanged = collectionChanged;
}

/// <summary>
/// Gets the type of action performed.
/// Allowed values are <see cref="NotifyCollectionChangedAction.Add"/> and <see cref="NotifyCollectionChangedAction.Remove"/>.
/// </summary>
public NotifyCollectionChangedAction Action { get; }

/// <summary>
/// Gets the added or removed item (if dictionary, value only).
/// </summary>
public TValue? Item { get; }

/// <summary>
/// Gets the previous value. Only valid if <see cref="Action"/> is <see cref="NotifyCollectionChangedAction.Add"/> and <see cref="NotifyCollectionChangedAction.Remove"/>
/// </summary>
public TValue? OldItem { get; }

/// <summary>Gets the added or removed key (if dictionary).</summary>
public TKey Key { get; }

/// <summary>
/// Gets the index in the collection (if applicable).
/// </summary>
public int Index { get; }

/// <summary>
/// Gets a value indicating whether [collection changed (not a replacement but real insertion/removal)].
/// </summary>
/// <value>
/// <c>true</c> if [collection changed]; otherwise, <c>false</c>.
/// </value>
public bool CollectionChanged { get; }
}
Original file line number Diff line number Diff line change
Expand Up @@ -365,7 +365,7 @@ private NavigationMeshDebugVisual CreateDebugVisual(NavigationMesh navigationMes
return ret;
}

private void GameSystemsOnCollectionChanged(object sender, TrackingCollectionChangedEventArgs e)
private void GameSystemsOnCollectionChanged(object sender, TrackingCollectionChangedEventArgs<IGameSystemBase> e)
{
if (dynamicNavigationMeshSystem != null)
return;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -45,13 +45,13 @@ internal BlockNodeVertex(VisualScriptBlockViewModel viewModel)
viewModel.Block.Slots.CollectionChanged += Slots_CollectionChanged;
}

private void Slots_CollectionChanged(object sender, TrackingCollectionChangedEventArgs e)
private void Slots_CollectionChanged(object sender, TrackingCollectionChangedEventArgs<Slot> e)
{
switch (e.Action)
{
case NotifyCollectionChangedAction.Add:
{
var slot = (Slot)e.Item;
var slot = e.Item;
var slots = slot.Direction == SlotDirection.Input ? InputSlots : OutputSlots;
var slotViewModel = new VisualScriptSlotViewModel(ViewModel, slot);
slots.Add(slotViewModel);
Expand Down
Loading
Loading