diff --git a/.gitignore b/.gitignore index 419f92a..b611ba2 100644 --- a/.gitignore +++ b/.gitignore @@ -117,29 +117,11 @@ nuget/*.unitypackage # Unity -src/MessagePipe.Unity/Library/* -src/MessagePipe.Unity/Temp/* -src/MessagePipe.Unity/Logs/* -src/MessagePipe.Unity/Assembly-CSharp.csproj - -src/MessagePipe.Unity/MessagePipe.csproj - -src/MessagePipe.Unity/MessagePipe.Unity.sln - -src/MessagePipe.Unity/MessagePipe.VContainer.csproj - -src/MessagePipe.Unity/RuntimeUnitTestToolkit.csproj - -src/MessagePipe.Unity/Tests.csproj - -src/MessagePipe.Unity/Assembly-CSharp-firstpass.csproj - -src/MessagePipe.Unity/MessagePipe.Zenject.csproj - -src/MessagePipe.Unity/Assembly-CSharp-Editor.csproj - -src/MessagePipe.Unity/Zenject-Editor.csproj - -src/MessagePipe.Unity/Zenject.csproj - -src/MessagePipe.Unity/MessagePipe.Editor.csproj +src/ObservableCollections.Unity/Library/* +src/ObservableCollections.Unity/Temp/* +src/ObservableCollections.Unity/Logs/* +src/ObservableCollections.Unity/Assembly-CSharp.csproj +src/ObservableCollections.Unity/ObservableCollections.csproj +src/ObservableCollections.Unity/ObservableCollections.Unity.sln +src/ObservableCollections.Unity/Assembly-CSharp-firstpass.csproj +src/ObservableCollections.Unity/Assembly-CSharp-Editor.csproj \ No newline at end of file diff --git a/src/ObservableCollections.Unity/Assets/Plugins.meta b/src/ObservableCollections.Unity/Assets/Plugins.meta new file mode 100644 index 0000000..25f3021 --- /dev/null +++ b/src/ObservableCollections.Unity/Assets/Plugins.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: 58b896faeb4c95c4a939b19f8dbf74ab +folderAsset: yes +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/src/ObservableCollections.Unity/Assets/Plugins/ObservableCollections.meta b/src/ObservableCollections.Unity/Assets/Plugins/ObservableCollections.meta new file mode 100644 index 0000000..47378b7 --- /dev/null +++ b/src/ObservableCollections.Unity/Assets/Plugins/ObservableCollections.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: 791bdc9996d0f5c42bea2aa68c95dced +folderAsset: yes +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/src/ObservableCollections.Unity/Assets/Plugins/ObservableCollections/Runtime.meta b/src/ObservableCollections.Unity/Assets/Plugins/ObservableCollections/Runtime.meta new file mode 100644 index 0000000..32fa8dc --- /dev/null +++ b/src/ObservableCollections.Unity/Assets/Plugins/ObservableCollections/Runtime.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: a2ca402f41ce60d4c95ee34b6715ee8a +folderAsset: yes +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/src/ObservableCollections.Unity/Assets/Plugins/ObservableCollections/Runtime/FreezedDictionary.cs b/src/ObservableCollections.Unity/Assets/Plugins/ObservableCollections/Runtime/FreezedDictionary.cs new file mode 100644 index 0000000..165149e --- /dev/null +++ b/src/ObservableCollections.Unity/Assets/Plugins/ObservableCollections/Runtime/FreezedDictionary.cs @@ -0,0 +1,59 @@ + + +using ObservableCollections.Internal; +using System; +using System.Collections; +using System.Collections.Generic; +using System.Diagnostics.CodeAnalysis; + +namespace ObservableCollections +{ + public sealed class FreezedDictionary : IReadOnlyDictionary, IFreezedCollection> + + { + readonly IReadOnlyDictionary dictionary; + + public FreezedDictionary(IReadOnlyDictionary dictionary) + { + this.dictionary = dictionary; + } + + public TValue this[TKey key] => dictionary[key]; + + public IEnumerable Keys => dictionary.Keys; + + public IEnumerable Values => dictionary.Values; + + public int Count => dictionary.Count; + + public bool ContainsKey(TKey key) + { + return dictionary.ContainsKey(key); + } + + public IEnumerator> GetEnumerator() + { + return dictionary.GetEnumerator(); + } + + public bool TryGetValue(TKey key, [MaybeNullWhen(false)] out TValue value) + { + return dictionary.TryGetValue(key, out value); + } + + IEnumerator IEnumerable.GetEnumerator() + { + return ((IEnumerable)dictionary).GetEnumerator(); + } + + public ISynchronizedView, TView> CreateView(Func, TView> transform, bool reverse = false) + { + return new FreezedView, TView>(dictionary, transform, reverse); + } + + public ISortableSynchronizedView, TView> CreateSortableView(Func, TView> transform) + { + return new FreezedSortableView, TView>(dictionary, transform); + } + } +} \ No newline at end of file diff --git a/src/ObservableCollections.Unity/Assets/Plugins/ObservableCollections/Runtime/FreezedDictionary.cs.meta b/src/ObservableCollections.Unity/Assets/Plugins/ObservableCollections/Runtime/FreezedDictionary.cs.meta new file mode 100644 index 0000000..d3f5ff1 --- /dev/null +++ b/src/ObservableCollections.Unity/Assets/Plugins/ObservableCollections/Runtime/FreezedDictionary.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 1470201cf9f7db249b7f441e515b77b0 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/src/ObservableCollections.Unity/Assets/Plugins/ObservableCollections/Runtime/FreezedList.cs b/src/ObservableCollections.Unity/Assets/Plugins/ObservableCollections/Runtime/FreezedList.cs new file mode 100644 index 0000000..f7666e5 --- /dev/null +++ b/src/ObservableCollections.Unity/Assets/Plugins/ObservableCollections/Runtime/FreezedList.cs @@ -0,0 +1,61 @@ +using ObservableCollections.Internal; +using System; +using System.Collections; +using System.Collections.Generic; +using System.Linq; + +namespace ObservableCollections +{ + public sealed class FreezedList : IReadOnlyList, IFreezedCollection + { + readonly IReadOnlyList list; + + public T this[int index] + { + get + { + return list[index]; + } + } + + public int Count + { + get + { + return list.Count; + } + } + + public bool IsReadOnly => true; + + public FreezedList(IReadOnlyList list) + { + this.list = list; + } + + public ISynchronizedView CreateView(Func transform, bool reverse = false) + { + return new FreezedView(list, transform, reverse); + } + + public ISortableSynchronizedView CreateSortableView(Func transform) + { + return new FreezedSortableView(list, transform); + } + + public bool Contains(T item) + { + return list.Contains(item); + } + + public IEnumerator GetEnumerator() + { + return list.GetEnumerator(); + } + + IEnumerator IEnumerable.GetEnumerator() + { + return GetEnumerator(); + } + } +} \ No newline at end of file diff --git a/src/ObservableCollections.Unity/Assets/Plugins/ObservableCollections/Runtime/FreezedList.cs.meta b/src/ObservableCollections.Unity/Assets/Plugins/ObservableCollections/Runtime/FreezedList.cs.meta new file mode 100644 index 0000000..99138de --- /dev/null +++ b/src/ObservableCollections.Unity/Assets/Plugins/ObservableCollections/Runtime/FreezedList.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 3207eb016a325514b8f137b4cf17df40 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/src/ObservableCollections.Unity/Assets/Plugins/ObservableCollections/Runtime/IObservableCollection.cs b/src/ObservableCollections.Unity/Assets/Plugins/ObservableCollections/Runtime/IObservableCollection.cs new file mode 100644 index 0000000..8389311 --- /dev/null +++ b/src/ObservableCollections.Unity/Assets/Plugins/ObservableCollections/Runtime/IObservableCollection.cs @@ -0,0 +1,118 @@ +using ObservableCollections.Internal; +using System; +using System.Collections.Generic; +using System.Collections.Specialized; +using System.ComponentModel; + +namespace ObservableCollections +{ + public delegate void NotifyCollectionChangedEventHandler(in NotifyCollectionChangedEventArgs e); + + public interface IObservableCollection : IReadOnlyCollection + { + event NotifyCollectionChangedEventHandler CollectionChanged; + object SyncRoot { get; } + ISynchronizedView CreateView(Func transform, bool reverse = false); + } + + public interface IFreezedCollection + { + ISynchronizedView CreateView(Func transform, bool reverse = false); + ISortableSynchronizedView CreateSortableView(Func transform); + } + + public interface ISynchronizedView : IReadOnlyCollection<(T Value, TView View)>, IDisposable + { + object SyncRoot { get; } + + event NotifyCollectionChangedEventHandler RoutingCollectionChanged; + event Action CollectionStateChanged; + + void AttachFilter(ISynchronizedViewFilter filter); + void ResetFilter(Action resetAction); + INotifyCollectionChangedSynchronizedView WithINotifyCollectionChanged(); + } + + public interface ISortableSynchronizedView : ISynchronizedView + { + void Sort(IComparer comparer); + void Sort(IComparer viewComparer); + } + + // will be implemented in the future? + //public interface IGroupedSynchoronizedView : ILookup, ISynchronizedView + //{ + //} + + public interface INotifyCollectionChangedSynchronizedView : ISynchronizedView, INotifyCollectionChanged, INotifyPropertyChanged + { + } + + public static class ObservableCollectionsExtensions + { + public static ISynchronizedView CreateSortedView(this IObservableCollection source, Func identitySelector, Func transform, IComparer comparer) + + { + return new SortedView(source, identitySelector, transform, comparer); + } + + public static ISynchronizedView CreateSortedView(this IObservableCollection source, Func identitySelector, Func transform, IComparer viewComparer) + + { + return new SortedViewViewComparer(source, identitySelector, transform, viewComparer); + } + + public static ISynchronizedView CreateSortedView(this IObservableCollection source, Func identitySelector, Func transform, Func compareSelector, bool ascending = true) + + { + return source.CreateSortedView(identitySelector, transform, new AnonymousComparer(compareSelector, ascending)); + } + + public static ISortableSynchronizedView CreateSortableView(this IFreezedCollection source, Func transform, IComparer initialSort) + { + var view = source.CreateSortableView(transform); + view.Sort(initialSort); + return view; + } + + public static ISortableSynchronizedView CreateSortableView(this IFreezedCollection source, Func transform, IComparer initialViewSort) + { + var view = source.CreateSortableView(transform); + view.Sort(initialViewSort); + return view; + } + + public static ISortableSynchronizedView CreateSortableView(this IFreezedCollection source, Func transform, Func initialCompareSelector, bool ascending = true) + { + var view = source.CreateSortableView(transform); + view.Sort(initialCompareSelector, ascending); + return view; + } + + public static void Sort(this ISortableSynchronizedView source, Func compareSelector, bool ascending = true) + { + source.Sort(new AnonymousComparer(compareSelector, ascending)); + } + + class AnonymousComparer : IComparer + { + readonly Func selector; + readonly int f; + + public AnonymousComparer(Func selector, bool ascending) + { + this.selector = selector; + this.f = ascending ? 1 : -1; + } + + public int Compare(T x, T y) + { + if (x == null && y == null) return 0; + if (x == null) return 1 * f; + if (y == null) return -1 * f; + + return Comparer.Default.Compare(selector(x), selector(y)) * f; + } + } + } +} \ No newline at end of file diff --git a/src/ObservableCollections.Unity/Assets/Plugins/ObservableCollections/Runtime/IObservableCollection.cs.meta b/src/ObservableCollections.Unity/Assets/Plugins/ObservableCollections/Runtime/IObservableCollection.cs.meta new file mode 100644 index 0000000..ee265a8 --- /dev/null +++ b/src/ObservableCollections.Unity/Assets/Plugins/ObservableCollections/Runtime/IObservableCollection.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: bdb4cc0f6a8b396418c8250eeaf2d2c8 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/src/ObservableCollections.Unity/Assets/Plugins/ObservableCollections/Runtime/ISynchronizedViewFilter.cs b/src/ObservableCollections.Unity/Assets/Plugins/ObservableCollections/Runtime/ISynchronizedViewFilter.cs new file mode 100644 index 0000000..186eb1d --- /dev/null +++ b/src/ObservableCollections.Unity/Assets/Plugins/ObservableCollections/Runtime/ISynchronizedViewFilter.cs @@ -0,0 +1,121 @@ +using System; + +namespace ObservableCollections +{ + public interface ISynchronizedViewFilter + { + bool IsMatch(T value, TView view); + void WhenTrue(T value, TView view); + void WhenFalse(T value, TView view); + void OnCollectionChanged(ChangedKind changedKind, T value, TView view); + } + + public enum ChangedKind + { + Add, Remove, Move + } + + public class SynchronizedViewFilter : ISynchronizedViewFilter + { + public static readonly ISynchronizedViewFilter Null = new NullViewFilter(); + + readonly Func isMatch; + readonly Action whenTrue; + readonly Action whenFalse; + readonly Action onCollectionChanged; + + public SynchronizedViewFilter(Func isMatch, Action whenTrue, Action whenFalse, Action onCollectionChanged) + { + this.isMatch = isMatch; + this.whenTrue = whenTrue; + this.whenFalse = whenFalse; + this.onCollectionChanged = onCollectionChanged; + } + + public bool IsMatch(T value, TView view) => isMatch(value, view); + public void WhenFalse(T value, TView view) => whenFalse?.Invoke(value, view); + public void WhenTrue(T value, TView view) => whenTrue?.Invoke(value, view); + public void OnCollectionChanged(ChangedKind changedKind, T value, TView view) => onCollectionChanged?.Invoke(changedKind, value, view); + + class NullViewFilter : ISynchronizedViewFilter + { + public bool IsMatch(T value, TView view) => true; + public void WhenFalse(T value, TView view) { } + public void WhenTrue(T value, TView view) { } + public void OnCollectionChanged(ChangedKind changedKind, T value, TView view) { } + } + } + + public static class SynchronizedViewFilterExtensions + { + public static void AttachFilter(this ISynchronizedView source, Func filter) + { + source.AttachFilter(new SynchronizedViewFilter(filter, null, null, null)); + } + + public static void AttachFilter(this ISynchronizedView source, Func isMatch, Action whenTrue, Action whenFalse) + { + source.AttachFilter(new SynchronizedViewFilter(isMatch, whenTrue, whenFalse, null)); + } + + public static void AttachFilter(this ISynchronizedView source, Func isMatch, Action whenTrue, Action whenFalse, Action onCollectionChanged) + { + source.AttachFilter(new SynchronizedViewFilter(isMatch, whenTrue, whenFalse, onCollectionChanged)); + } + + public static bool IsNullFilter(this ISynchronizedViewFilter filter) + { + return filter == SynchronizedViewFilter.Null; + } + + internal static void InvokeOnAdd(this ISynchronizedViewFilter filter, (T value, TView view) value) + { + InvokeOnAdd(filter, value.value, value.view); + } + + internal static void InvokeOnAdd(this ISynchronizedViewFilter filter, T value, TView view) + { + if (filter.IsMatch(value, view)) + { + filter.WhenTrue(value, view); + } + else + { + filter.WhenFalse(value, view); + } + filter.OnCollectionChanged(ChangedKind.Add, value, view); + } + + internal static void InvokeOnRemove(this ISynchronizedViewFilter filter, (T value, TView view) value) + { + InvokeOnRemove(filter, value.value, value.view); + } + + internal static void InvokeOnRemove(this ISynchronizedViewFilter filter, T value, TView view) + { + filter.OnCollectionChanged(ChangedKind.Remove, value, view); + } + + internal static void InvokeOnMove(this ISynchronizedViewFilter filter, (T value, TView view) value) + { + InvokeOnMove(filter, value.value, value.view); + } + + internal static void InvokeOnMove(this ISynchronizedViewFilter filter, T value, TView view) + { + filter.OnCollectionChanged(ChangedKind.Move, value, view); + } + + internal static void InvokeOnAttach(this ISynchronizedViewFilter filter, T value, TView view) + { + if (filter.IsMatch(value, view)) + { + filter.WhenTrue(value, view); + } + else + { + filter.WhenFalse(value, view); + } + } + } +} \ No newline at end of file diff --git a/src/ObservableCollections.Unity/Assets/Plugins/ObservableCollections/Runtime/ISynchronizedViewFilter.cs.meta b/src/ObservableCollections.Unity/Assets/Plugins/ObservableCollections/Runtime/ISynchronizedViewFilter.cs.meta new file mode 100644 index 0000000..973e104 --- /dev/null +++ b/src/ObservableCollections.Unity/Assets/Plugins/ObservableCollections/Runtime/ISynchronizedViewFilter.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 0ffdcef914adf8848a485ac600cc41c4 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/src/ObservableCollections.Unity/Assets/Plugins/ObservableCollections/Runtime/Internal.meta b/src/ObservableCollections.Unity/Assets/Plugins/ObservableCollections/Runtime/Internal.meta new file mode 100644 index 0000000..6f7adde --- /dev/null +++ b/src/ObservableCollections.Unity/Assets/Plugins/ObservableCollections/Runtime/Internal.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: 0b3e01975c6d488409ce0ac753c89870 +folderAsset: yes +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/src/ObservableCollections.Unity/Assets/Plugins/ObservableCollections/Runtime/Internal/CloneCollection.cs b/src/ObservableCollections.Unity/Assets/Plugins/ObservableCollections/Runtime/Internal/CloneCollection.cs new file mode 100644 index 0000000..c418aad --- /dev/null +++ b/src/ObservableCollections.Unity/Assets/Plugins/ObservableCollections/Runtime/Internal/CloneCollection.cs @@ -0,0 +1,130 @@ +using System; +using System.Buffers; +using System.Collections; +using System.Collections.Generic; +using System.Runtime.CompilerServices; + +namespace ObservableCollections.Internal +{ + /// + /// ReadOnly cloned collection. + /// + internal struct CloneCollection : IDisposable + { + T[] array; + int length; + + public ReadOnlySpan Span => array.AsSpan(0, length); + + public IEnumerable AsEnumerable() => new EnumerableCollection(array, length); + + public CloneCollection(T item) + { + this.array = ArrayPool.Shared.Rent(1); + this.length = 1; + } + + public CloneCollection(IEnumerable source) + { + if (source.TryGetNonEnumeratedCount(out var count)) + { + var array = ArrayPool.Shared.Rent(count); + + if (source is ICollection c) + { + c.CopyTo(array, 0); + } + else + { + var i = 0; + foreach (var item in source) + { + array[i++] = item; + } + } + this.array = array; + this.length = count; + } + else + { + var array = ArrayPool.Shared.Rent(count); + + var i = 0; + foreach (var item in source) + { + TryEnsureCapacity(ref array, i); + array[i++] = item; + } + this.array = array; + this.length = i; + } + } + + public CloneCollection(ReadOnlySpan source) + { + var array = ArrayPool.Shared.Rent(source.Length); + source.CopyTo(array); + this.array = array; + this.length = source.Length; + } + + static void TryEnsureCapacity(ref T[] array, int index) + { + if (array.Length == index) + { + ArrayPool.Shared.Return(array, RuntimeHelpersEx.IsReferenceOrContainsReferences()); + } + array = ArrayPool.Shared.Rent(index * 2); + } + + public void Dispose() + { + if (array != null) + { + ArrayPool.Shared.Return(array, RuntimeHelpersEx.IsReferenceOrContainsReferences()); + array = null!; + } + } + + // Optimize to use Count and CopyTo + class EnumerableCollection : ICollection + { + readonly T[] array; + readonly int count; + + public EnumerableCollection(T[] array, int count) + { + if (array == null) + { + this.array = Array.Empty(); + this.count = 0; + } + else + { + this.array = array; + this.count = count; + } + } + + public int Count => count; + + public bool IsReadOnly => true; + + public void Add(T item) => throw new NotSupportedException(); + public void Clear() => throw new NotSupportedException(); + public bool Contains(T item) => throw new NotSupportedException(); + public void CopyTo(T[] dest, int destIndex) => array.CopyTo(dest, destIndex); + + public IEnumerator GetEnumerator() + { + for (int i = 0; i < count; i++) + { + yield return array[i]; + } + } + + public bool Remove(T item) => throw new NotSupportedException(); + IEnumerator IEnumerable.GetEnumerator() => GetEnumerator(); + } + } +} \ No newline at end of file diff --git a/src/ObservableCollections.Unity/Assets/Plugins/ObservableCollections/Runtime/Internal/CloneCollection.cs.meta b/src/ObservableCollections.Unity/Assets/Plugins/ObservableCollections/Runtime/Internal/CloneCollection.cs.meta new file mode 100644 index 0000000..267a508 --- /dev/null +++ b/src/ObservableCollections.Unity/Assets/Plugins/ObservableCollections/Runtime/Internal/CloneCollection.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 7b9643f0fa1bf7447961693f16cb557b +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/src/ObservableCollections.Unity/Assets/Plugins/ObservableCollections/Runtime/Internal/FreezedView.cs b/src/ObservableCollections.Unity/Assets/Plugins/ObservableCollections/Runtime/Internal/FreezedView.cs new file mode 100644 index 0000000..1c5f909 --- /dev/null +++ b/src/ObservableCollections.Unity/Assets/Plugins/ObservableCollections/Runtime/Internal/FreezedView.cs @@ -0,0 +1,205 @@ +#pragma warning disable CS0067 + +using System; +using System.Collections; +using System.Collections.Generic; +using System.Collections.Specialized; +using System.Linq; + +namespace ObservableCollections.Internal +{ + internal sealed class FreezedView : ISynchronizedView + { + readonly bool reverse; + readonly List<(T, TView)> list; + + ISynchronizedViewFilter filter; + + public event Action CollectionStateChanged; + public event NotifyCollectionChangedEventHandler RoutingCollectionChanged; + + public object SyncRoot { get; } = new object(); + + public FreezedView(IEnumerable source, Func selector, bool reverse) + { + this.reverse = reverse; + this.filter = SynchronizedViewFilter.Null; + this.list = source.Select(x => (x, selector(x))).ToList(); + } + + public int Count + { + get + { + lock (SyncRoot) + { + return list.Count; + } + } + } + + public void AttachFilter(ISynchronizedViewFilter filter) + { + lock (SyncRoot) + { + this.filter = filter; + foreach (var (value, view) in list) + { + filter.InvokeOnAttach(value, view); + } + } + } + + public void ResetFilter(Action resetAction) + { + lock (SyncRoot) + { + this.filter = SynchronizedViewFilter.Null; + if (resetAction != null) + { + foreach (var (item, view) in list) + { + resetAction(item, view); + } + } + } + } + + public IEnumerator<(T, TView)> GetEnumerator() + { + if (!reverse) + { + return new SynchronizedViewEnumerator(SyncRoot, list.GetEnumerator(), filter); + } + else + { + return new SynchronizedViewEnumerator(SyncRoot, list.AsEnumerable().Reverse().GetEnumerator(), filter); + } + } + + IEnumerator IEnumerable.GetEnumerator() => GetEnumerator(); + + public void Dispose() + { + + } + + public INotifyCollectionChangedSynchronizedView WithINotifyCollectionChanged() + { + return new NotifyCollectionChangedSynchronizedView(this); + } + } + + internal sealed class FreezedSortableView : ISortableSynchronizedView + { + readonly (T, TView)[] array; + + ISynchronizedViewFilter filter; + + public event Action CollectionStateChanged; + public event NotifyCollectionChangedEventHandler RoutingCollectionChanged; + + public object SyncRoot { get; } = new object(); + + public FreezedSortableView(IEnumerable source, Func selector) + { + this.filter = SynchronizedViewFilter.Null; + this.array = source.Select(x => (x, selector(x))).ToArray(); + } + + public int Count + { + get + { + lock (SyncRoot) + { + return array.Length; + } + } + } + + public void AttachFilter(ISynchronizedViewFilter filter) + { + lock (SyncRoot) + { + this.filter = filter; + foreach (var (value, view) in array) + { + filter.InvokeOnAttach(value, view); + } + } + } + + public void ResetFilter(Action resetAction) + { + lock (SyncRoot) + { + this.filter = SynchronizedViewFilter.Null; + if (resetAction != null) + { + foreach (var (item, view) in array) + { + resetAction(item, view); + } + } + } + } + + public IEnumerator<(T, TView)> GetEnumerator() + { + return new SynchronizedViewEnumerator(SyncRoot, array.AsEnumerable().GetEnumerator(), filter); + } + + IEnumerator IEnumerable.GetEnumerator() => GetEnumerator(); + + public void Dispose() + { + + } + + public void Sort(IComparer comparer) + { + Array.Sort(array, new TComparer(comparer)); + } + + public void Sort(IComparer viewComparer) + { + Array.Sort(array, new TViewComparer(viewComparer)); + } + + public INotifyCollectionChangedSynchronizedView WithINotifyCollectionChanged() + { + return new NotifyCollectionChangedSynchronizedView(this); + } + + class TComparer : IComparer<(T, TView)> + { + readonly IComparer comparer; + + public TComparer(IComparer comparer) + { + this.comparer = comparer; + } + + public int Compare((T, TView) x, (T, TView) y) + { + return comparer.Compare(x.Item1, y.Item1); + } + } + + class TViewComparer : IComparer<(T, TView)> + { + readonly IComparer comparer; + + public TViewComparer(IComparer comparer) + { + this.comparer = comparer; + } + + public int Compare((T, TView) x, (T, TView) y) + { + return comparer.Compare(x.Item2, y.Item2); + } + } + } +} \ No newline at end of file diff --git a/src/ObservableCollections.Unity/Assets/Plugins/ObservableCollections/Runtime/Internal/FreezedView.cs.meta b/src/ObservableCollections.Unity/Assets/Plugins/ObservableCollections/Runtime/Internal/FreezedView.cs.meta new file mode 100644 index 0000000..70fe6fe --- /dev/null +++ b/src/ObservableCollections.Unity/Assets/Plugins/ObservableCollections/Runtime/Internal/FreezedView.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: a768cdaf951b40345b6d5b21ade3e282 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/src/ObservableCollections.Unity/Assets/Plugins/ObservableCollections/Runtime/Internal/NotifyCollectionChangedSynchronizedView.cs b/src/ObservableCollections.Unity/Assets/Plugins/ObservableCollections/Runtime/Internal/NotifyCollectionChangedSynchronizedView.cs new file mode 100644 index 0000000..e042955 --- /dev/null +++ b/src/ObservableCollections.Unity/Assets/Plugins/ObservableCollections/Runtime/Internal/NotifyCollectionChangedSynchronizedView.cs @@ -0,0 +1,70 @@ +using System; +using System.Collections; +using System.Collections.Generic; +using System.Collections.Specialized; +using System.ComponentModel; + +namespace ObservableCollections.Internal +{ + internal class NotifyCollectionChangedSynchronizedView : INotifyCollectionChangedSynchronizedView + { + readonly ISynchronizedView parent; + static readonly PropertyChangedEventArgs CountPropertyChangedEventArgs = new PropertyChangedEventArgs("Count"); + + public NotifyCollectionChangedSynchronizedView(ISynchronizedView parent) + { + this.parent = parent; + this.parent.RoutingCollectionChanged += Parent_RoutingCollectionChanged; + } + + private void Parent_RoutingCollectionChanged(in NotifyCollectionChangedEventArgs e) + { + CollectionChanged?.Invoke(this, e.ToStandardEventArgs()); + + switch (e.Action) + { + // add, remove, reset will change the count. + case NotifyCollectionChangedAction.Add: + case NotifyCollectionChangedAction.Remove: + case NotifyCollectionChangedAction.Reset: + PropertyChanged?.Invoke(this, CountPropertyChangedEventArgs); + break; + case NotifyCollectionChangedAction.Replace: + case NotifyCollectionChangedAction.Move: + default: + break; + } + } + + public object SyncRoot => parent.SyncRoot; + + public int Count => parent.Count; + + public event NotifyCollectionChangedEventHandler CollectionChanged; + public event PropertyChangedEventHandler PropertyChanged; + + public event Action CollectionStateChanged + { + add { parent.CollectionStateChanged += value; } + remove { parent.CollectionStateChanged -= value; } + } + + public event NotifyCollectionChangedEventHandler RoutingCollectionChanged + { + add { parent.RoutingCollectionChanged += value; } + remove { parent.RoutingCollectionChanged -= value; } + } + + public void AttachFilter(ISynchronizedViewFilter filter) => parent.AttachFilter(filter); + public void ResetFilter(Action resetAction) => parent.ResetFilter(resetAction); + public INotifyCollectionChangedSynchronizedView WithINotifyCollectionChanged() => this; + public void Dispose() + { + this.parent.RoutingCollectionChanged -= Parent_RoutingCollectionChanged; + parent.Dispose(); + } + + public IEnumerator<(T, TView)> GetEnumerator() => parent.GetEnumerator(); + IEnumerator IEnumerable.GetEnumerator() => parent.GetEnumerator(); + } +} \ No newline at end of file diff --git a/src/ObservableCollections.Unity/Assets/Plugins/ObservableCollections/Runtime/Internal/NotifyCollectionChangedSynchronizedView.cs.meta b/src/ObservableCollections.Unity/Assets/Plugins/ObservableCollections/Runtime/Internal/NotifyCollectionChangedSynchronizedView.cs.meta new file mode 100644 index 0000000..25bee10 --- /dev/null +++ b/src/ObservableCollections.Unity/Assets/Plugins/ObservableCollections/Runtime/Internal/NotifyCollectionChangedSynchronizedView.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: b0e2d983b8f809a45b60f610d0542761 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/src/ObservableCollections.Unity/Assets/Plugins/ObservableCollections/Runtime/Internal/ResizableArray.cs b/src/ObservableCollections.Unity/Assets/Plugins/ObservableCollections/Runtime/Internal/ResizableArray.cs new file mode 100644 index 0000000..f36168c --- /dev/null +++ b/src/ObservableCollections.Unity/Assets/Plugins/ObservableCollections/Runtime/Internal/ResizableArray.cs @@ -0,0 +1,56 @@ +using System; +using System.Buffers; +using System.Diagnostics.CodeAnalysis; +using System.Runtime.CompilerServices; + +namespace ObservableCollections.Internal +{ + // internal ref struct ResizableArray + internal struct ResizableArray : IDisposable + { + T[] array; + int count; + + public ReadOnlySpan Span => array.AsSpan(0, count); + + public ResizableArray(int initialCapacity) + { + array = ArrayPool.Shared.Rent(initialCapacity); + count = 0; + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public void Add(T item) + { + if (array == null) Throw(); + if (array.Length == count) + { + EnsureCapacity(); + } + array[count++] = item; + } + + [MethodImpl(MethodImplOptions.NoInlining)] + void EnsureCapacity() + { + var newArray = array.AsSpan().ToArray(); + ArrayPool.Shared.Return(array, RuntimeHelpersEx.IsReferenceOrContainsReferences()); + array = newArray; + } + + public void Dispose() + { + if (array != null) + { + ArrayPool.Shared.Return(array, RuntimeHelpersEx.IsReferenceOrContainsReferences()); + array = null; + } + } + + [DoesNotReturn] + void Throw() + { + throw new ObjectDisposedException("ResizableArray"); + } + } +} diff --git a/src/ObservableCollections.Unity/Assets/Plugins/ObservableCollections/Runtime/Internal/ResizableArray.cs.meta b/src/ObservableCollections.Unity/Assets/Plugins/ObservableCollections/Runtime/Internal/ResizableArray.cs.meta new file mode 100644 index 0000000..57a4711 --- /dev/null +++ b/src/ObservableCollections.Unity/Assets/Plugins/ObservableCollections/Runtime/Internal/ResizableArray.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: b1477839cc1e36a4187e91e4fd001dd5 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/src/ObservableCollections.Unity/Assets/Plugins/ObservableCollections/Runtime/Internal/SortedView.cs b/src/ObservableCollections.Unity/Assets/Plugins/ObservableCollections/Runtime/Internal/SortedView.cs new file mode 100644 index 0000000..1d4a0c0 --- /dev/null +++ b/src/ObservableCollections.Unity/Assets/Plugins/ObservableCollections/Runtime/Internal/SortedView.cs @@ -0,0 +1,216 @@ +using System; +using System.Collections; +using System.Collections.Generic; +using System.Collections.Specialized; +using System.Linq; + +namespace ObservableCollections.Internal +{ + internal class SortedView : ISynchronizedView + + { + readonly IObservableCollection source; + readonly Func transform; + readonly Func identitySelector; + readonly SortedDictionary<(T Value, TKey Key), (T Value, TView View)> dict; + + ISynchronizedViewFilter filter; + + public event NotifyCollectionChangedEventHandler RoutingCollectionChanged; + public event Action CollectionStateChanged; + + public object SyncRoot { get; } = new object(); + + public SortedView(IObservableCollection source, Func identitySelector, Func transform, IComparer comparer) + { + this.source = source; + this.identitySelector = identitySelector; + this.transform = transform; + this.filter = SynchronizedViewFilter.Null; + lock (source.SyncRoot) + { + var dict = new SortedDictionary<(T, TKey), (T, TView)>(new Comparer(comparer)); + foreach (var v in source) + { + dict.Add((v, identitySelector(v)), (v, transform(v))); + } + + this.dict = dict; + this.source.CollectionChanged += SourceCollectionChanged; + } + } + + public int Count + { + get + { + lock (SyncRoot) + { + return dict.Count; + } + } + } + + public void AttachFilter(ISynchronizedViewFilter filter) + { + lock (SyncRoot) + { + this.filter = filter; + foreach (var (_, (value, view)) in dict) + { + filter.InvokeOnAttach(value, view); + } + } + } + + public void ResetFilter(Action resetAction) + { + lock (SyncRoot) + { + this.filter = SynchronizedViewFilter.Null; + if (resetAction != null) + { + foreach (var (_, (value, view)) in dict) + { + resetAction(value, view); + } + } + } + } + + public INotifyCollectionChangedSynchronizedView WithINotifyCollectionChanged() + { + lock (SyncRoot) + { + return new NotifyCollectionChangedSynchronizedView(this); + } + } + + public IEnumerator<(T, TView)> GetEnumerator() + { + return new SynchronizedViewEnumerator(SyncRoot, dict.Select(x => x.Value).GetEnumerator(), filter); + } + + IEnumerator IEnumerable.GetEnumerator() => GetEnumerator(); + + public void Dispose() + { + this.source.CollectionChanged -= SourceCollectionChanged; + } + + private void SourceCollectionChanged(in NotifyCollectionChangedEventArgs e) + { + lock (SyncRoot) + { + switch (e.Action) + { + case NotifyCollectionChangedAction.Add: + { + // Add, Insert + if (e.IsSingleItem) + { + var value = e.NewItem; + var view = transform(value); + var id = identitySelector(value); + dict.Add((value, id), (value, view)); + filter.InvokeOnAdd(value, view); + } + else + { + foreach (var value in e.NewItems) + { + var view = transform(value); + var id = identitySelector(value); + dict.Add((value, id), (value, view)); + filter.InvokeOnAdd(value, view); + } + } + } + break; + case NotifyCollectionChangedAction.Remove: + { + if (e.IsSingleItem) + { + var value = e.OldItem; + var id = identitySelector(value); + dict.Remove((value, id), out var v); + filter.InvokeOnRemove(v.Value, v.View); + } + else + { + foreach (var value in e.OldItems) + { + var id = identitySelector(value); + dict.Remove((value, id), out var v); + filter.InvokeOnRemove(v.Value, v.View); + } + } + } + break; + case NotifyCollectionChangedAction.Replace: + // ReplaceRange is not supported in all ObservableCollections collections + // Replace is remove old item and insert new item. + { + var oldValue = e.OldItem; + dict.Remove((oldValue, identitySelector(oldValue)), out var oldView); + + var value = e.NewItem; + var view = transform(value); + var id = identitySelector(value); + dict.Add((value, id), (value, view)); + + filter.InvokeOnRemove(oldView); + filter.InvokeOnAdd(value, view); + } + break; + case NotifyCollectionChangedAction.Move: + { + // Move(index change) does not affect sorted list. + var oldValue = e.OldItem; + if (dict.TryGetValue((oldValue, identitySelector(oldValue)), out var view)) + { + filter.InvokeOnMove(view); + } + } + break; + case NotifyCollectionChangedAction.Reset: + if (!filter.IsNullFilter()) + { + foreach (var item in dict) + { + filter.InvokeOnRemove(item.Value); + } + } + dict.Clear(); + break; + default: + break; + } + + RoutingCollectionChanged?.Invoke(e); + CollectionStateChanged?.Invoke(e.Action); + } + } + + sealed class Comparer : IComparer<(T value, TKey id)> + { + readonly IComparer comparer; + + public Comparer(IComparer comparer) + { + this.comparer = comparer; + } + + public int Compare((T value, TKey id) x, (T value, TKey id) y) + { + var compare = comparer.Compare(x.value, y.value); + if (compare == 0) + { + compare = Comparer.Default.Compare(x.id, y.id); + } + + return compare; + } + } + } +} diff --git a/src/ObservableCollections.Unity/Assets/Plugins/ObservableCollections/Runtime/Internal/SortedView.cs.meta b/src/ObservableCollections.Unity/Assets/Plugins/ObservableCollections/Runtime/Internal/SortedView.cs.meta new file mode 100644 index 0000000..a0eb7f7 --- /dev/null +++ b/src/ObservableCollections.Unity/Assets/Plugins/ObservableCollections/Runtime/Internal/SortedView.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 768c43ec998d9754d8cc13398ed69d39 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/src/ObservableCollections.Unity/Assets/Plugins/ObservableCollections/Runtime/Internal/SortedViewViewComparer.cs b/src/ObservableCollections.Unity/Assets/Plugins/ObservableCollections/Runtime/Internal/SortedViewViewComparer.cs new file mode 100644 index 0000000..105edb6 --- /dev/null +++ b/src/ObservableCollections.Unity/Assets/Plugins/ObservableCollections/Runtime/Internal/SortedViewViewComparer.cs @@ -0,0 +1,234 @@ +using System; +using System.Collections; +using System.Collections.Generic; +using System.Collections.Specialized; +using System.Linq; + +namespace ObservableCollections.Internal +{ + internal class SortedViewViewComparer : ISynchronizedView + + { + readonly IObservableCollection source; + readonly Func transform; + readonly Func identitySelector; + readonly Dictionary viewMap; // view-map needs to use in remove. + readonly SortedDictionary<(TView View, TKey Key), (T Value, TView View)> list; + + ISynchronizedViewFilter filter; + + public event NotifyCollectionChangedEventHandler RoutingCollectionChanged; + public event Action CollectionStateChanged; + + public object SyncRoot { get; } = new object(); + + public SortedViewViewComparer(IObservableCollection source, Func identitySelector, Func transform, IComparer comparer) + { + this.source = source; + this.identitySelector = identitySelector; + this.transform = transform; + this.filter = SynchronizedViewFilter.Null; + lock (source.SyncRoot) + { + var dict = new SortedDictionary<(TView, TKey), (T, TView)>(new Comparer(comparer)); + this.viewMap = new Dictionary(); + foreach (var value in source) + { + var view = transform(value); + var id = identitySelector(value); + dict.Add((view, id), (value, view)); + viewMap.Add(id, view); + } + this.list = dict; + this.source.CollectionChanged += SourceCollectionChanged; + } + } + + public int Count + { + get + { + lock (SyncRoot) + { + return list.Count; + } + } + } + + public void AttachFilter(ISynchronizedViewFilter filter) + { + lock (SyncRoot) + { + this.filter = filter; + foreach (var (_, (value, view)) in list) + { + filter.InvokeOnAttach(value, view); + } + } + } + + public void ResetFilter(Action resetAction) + { + lock (SyncRoot) + { + this.filter = SynchronizedViewFilter.Null; + if (resetAction != null) + { + foreach (var (_, (value, view)) in list) + { + resetAction(value, view); + } + } + } + } + + public INotifyCollectionChangedSynchronizedView WithINotifyCollectionChanged() + { + lock (SyncRoot) + { + return new NotifyCollectionChangedSynchronizedView(this); + } + } + + public IEnumerator<(T, TView)> GetEnumerator() + { + return new SynchronizedViewEnumerator(SyncRoot, list.Select(x => x.Value).GetEnumerator(), filter); + } + + IEnumerator IEnumerable.GetEnumerator() => GetEnumerator(); + + public void Dispose() + { + this.source.CollectionChanged -= SourceCollectionChanged; + } + + private void SourceCollectionChanged(in NotifyCollectionChangedEventArgs e) + { + lock (SyncRoot) + { + switch (e.Action) + { + case NotifyCollectionChangedAction.Add: + { + // Add, Insert + if (e.IsSingleItem) + { + var value = e.NewItem; + var view = transform(value); + var id = identitySelector(value); + list.Add((view, id), (value, view)); + viewMap.Add(id, view); + filter.InvokeOnAdd(value, view); + } + else + { + foreach (var value in e.NewItems) + { + var view = transform(value); + var id = identitySelector(value); + list.Add((view, id), (value, view)); + viewMap.Add(id, view); + filter.InvokeOnAdd(value, view); + } + } + } + break; + case NotifyCollectionChangedAction.Remove: + { + if (e.IsSingleItem) + { + var value = e.OldItem; + var id = identitySelector(value); + if (viewMap.Remove(id, out var view)) + { + list.Remove((view, id), out var v); + filter.InvokeOnRemove(v); + } + } + else + { + foreach (var value in e.OldItems) + { + var id = identitySelector(value); + if (viewMap.Remove(id, out var view)) + { + list.Remove((view, id), out var v); + filter.InvokeOnRemove(v); + } + } + } + } + break; + case NotifyCollectionChangedAction.Replace: + // Replace is remove old item and insert new item. + { + var oldValue = e.OldItem; + var oldKey = identitySelector(oldValue); + if (viewMap.Remove(oldKey, out var oldView)) + { + list.Remove((oldView, oldKey)); + filter.InvokeOnRemove(oldValue, oldView); + } + + var value = e.NewItem; + var view = transform(value); + var id = identitySelector(value); + list.Add((view, id), (value, view)); + viewMap.Add(id, view); + + filter.InvokeOnAdd(value, view); + } + break; + case NotifyCollectionChangedAction.Move: + // Move(index change) does not affect soreted dict. + { + var value = e.OldItem; + var key = identitySelector(value); + if (viewMap.TryGetValue(key, out var view)) + { + filter.InvokeOnMove(value, view); + } + } + break; + case NotifyCollectionChangedAction.Reset: + if (!filter.IsNullFilter()) + { + foreach (var item in list) + { + filter.InvokeOnRemove(item.Value); + } + } + list.Clear(); + viewMap.Clear(); + break; + default: + break; + } + + RoutingCollectionChanged?.Invoke(e); + CollectionStateChanged?.Invoke(e.Action); + } + } + + sealed class Comparer : IComparer<(TView view, TKey id)> + { + readonly IComparer comparer; + + public Comparer(IComparer comparer) + { + this.comparer = comparer; + } + + public int Compare((TView view, TKey id) x, (TView view, TKey id) y) + { + var compare = comparer.Compare(x.view, y.view); + if (compare == 0) + { + compare = Comparer.Default.Compare(x.id, y.id); + } + + return compare; + } + } + } +} \ No newline at end of file diff --git a/src/ObservableCollections.Unity/Assets/Plugins/ObservableCollections/Runtime/Internal/SortedViewViewComparer.cs.meta b/src/ObservableCollections.Unity/Assets/Plugins/ObservableCollections/Runtime/Internal/SortedViewViewComparer.cs.meta new file mode 100644 index 0000000..cacacbd --- /dev/null +++ b/src/ObservableCollections.Unity/Assets/Plugins/ObservableCollections/Runtime/Internal/SortedViewViewComparer.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 08e8703f31991094aa9216bf7166a549 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/src/ObservableCollections.Unity/Assets/Plugins/ObservableCollections/Runtime/Internal/SynchronizedEnumerator.cs b/src/ObservableCollections.Unity/Assets/Plugins/ObservableCollections/Runtime/Internal/SynchronizedEnumerator.cs new file mode 100644 index 0000000..79948a4 --- /dev/null +++ b/src/ObservableCollections.Unity/Assets/Plugins/ObservableCollections/Runtime/Internal/SynchronizedEnumerator.cs @@ -0,0 +1,46 @@ +using System.Collections; +using System.Collections.Generic; +using System.Threading; + +namespace ObservableCollections.Internal +{ + internal class SynchronizedEnumerator : IEnumerator + { + bool isDisposed; + readonly object gate; + readonly bool lockTaken; + readonly IEnumerator enumerator; + + public SynchronizedEnumerator(object gate, IEnumerator enumerator) + { + this.gate = gate; + this.enumerator = enumerator; + Monitor.Enter(gate, ref lockTaken); + } + + public T Current => enumerator.Current; + + object IEnumerator.Current => Current; + public bool MoveNext() => enumerator.MoveNext(); + public void Reset() => enumerator.Reset(); + + public void Dispose() + { + if (!isDisposed) + { + isDisposed = true; + try + { + enumerator.Dispose(); + } + finally + { + if (lockTaken) + { + Monitor.Exit(gate); + } + } + } + } + } +} diff --git a/src/ObservableCollections.Unity/Assets/Plugins/ObservableCollections/Runtime/Internal/SynchronizedEnumerator.cs.meta b/src/ObservableCollections.Unity/Assets/Plugins/ObservableCollections/Runtime/Internal/SynchronizedEnumerator.cs.meta new file mode 100644 index 0000000..edc4c63 --- /dev/null +++ b/src/ObservableCollections.Unity/Assets/Plugins/ObservableCollections/Runtime/Internal/SynchronizedEnumerator.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 08405416bc7c7e04e9bcaa4fa13c875f +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/src/ObservableCollections.Unity/Assets/Plugins/ObservableCollections/Runtime/Internal/SynchronizedViewEnumerator.cs b/src/ObservableCollections.Unity/Assets/Plugins/ObservableCollections/Runtime/Internal/SynchronizedViewEnumerator.cs new file mode 100644 index 0000000..fe0072e --- /dev/null +++ b/src/ObservableCollections.Unity/Assets/Plugins/ObservableCollections/Runtime/Internal/SynchronizedViewEnumerator.cs @@ -0,0 +1,63 @@ +using System; +using System.Collections; +using System.Collections.Generic; +using System.Threading; + +namespace ObservableCollections.Internal +{ + internal class SynchronizedViewEnumerator : IEnumerator<(T, TView)>, IDisposable + { + bool isDisposed; + readonly bool lockTaken; + readonly object gate; + readonly IEnumerator<(T, TView)> enumerator; + readonly ISynchronizedViewFilter filter; + (T, TView) current; + + public SynchronizedViewEnumerator(object gate, IEnumerator<(T, TView)> enumerator, ISynchronizedViewFilter filter) + { + this.gate = gate; + this.enumerator = enumerator; + this.filter = filter; + this.current = default; + this.isDisposed = false; + Monitor.Enter(gate, ref lockTaken); + } + + public (T, TView) Current => current; + object IEnumerator.Current => Current; + + public bool MoveNext() + { + while (enumerator.MoveNext()) + { + current = enumerator.Current; + if (filter.IsMatch(current.Item1, current.Item2)) + { + return true; + } + } + return false; + } + public void Reset() => throw new NotSupportedException(); + + public void Dispose() + { + if (!isDisposed) + { + isDisposed = true; + try + { + enumerator.Dispose(); + } + finally + { + if (lockTaken) + { + Monitor.Exit(gate); + } + } + } + } + } +} \ No newline at end of file diff --git a/src/ObservableCollections.Unity/Assets/Plugins/ObservableCollections/Runtime/Internal/SynchronizedViewEnumerator.cs.meta b/src/ObservableCollections.Unity/Assets/Plugins/ObservableCollections/Runtime/Internal/SynchronizedViewEnumerator.cs.meta new file mode 100644 index 0000000..f902998 --- /dev/null +++ b/src/ObservableCollections.Unity/Assets/Plugins/ObservableCollections/Runtime/Internal/SynchronizedViewEnumerator.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 98be7e821cfba26469f0911671fb24b6 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/src/ObservableCollections.Unity/Assets/Plugins/ObservableCollections/Runtime/NotifyCollectionChangedEventArgs.cs b/src/ObservableCollections.Unity/Assets/Plugins/ObservableCollections/Runtime/NotifyCollectionChangedEventArgs.cs new file mode 100644 index 0000000..a98194b --- /dev/null +++ b/src/ObservableCollections.Unity/Assets/Plugins/ObservableCollections/Runtime/NotifyCollectionChangedEventArgs.cs @@ -0,0 +1,125 @@ +using System; +using System.Collections.Specialized; + +namespace ObservableCollections +{ + /// + /// Contract: + /// IsSingleItem ? (NewItem, OldItem) : (NewItems, OldItems) + /// Action.Add + /// NewItem, NewItems, NewStartingIndex + /// Action.Remove + /// OldItem, OldItems, OldStartingIndex + /// Action.Replace + /// NewItem, NewItems, OldItem, OldItems, (NewStartingIndex, OldStartingIndex = samevalue) + /// Action.Move + /// NewStartingIndex, OldStartingIndex + /// Action.Reset + /// - + /// + public readonly ref struct NotifyCollectionChangedEventArgs + { + public readonly NotifyCollectionChangedAction Action; + public readonly bool IsSingleItem; + public readonly T NewItem; + public readonly T OldItem; + public readonly ReadOnlySpan NewItems; + public readonly ReadOnlySpan OldItems; + public readonly int NewStartingIndex; + public readonly int OldStartingIndex; + + public NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction action, bool isSingleItem, T newItem = default, T oldItem = default, ReadOnlySpan newItems = default, ReadOnlySpan oldItems = default, int newStartingIndex = -1, int oldStartingIndex = -1) + { + Action = action; + IsSingleItem = isSingleItem; + NewItem = newItem; + OldItem = oldItem; + NewItems = newItems; + OldItems = oldItems; + NewStartingIndex = newStartingIndex; + OldStartingIndex = oldStartingIndex; + } + + public NotifyCollectionChangedEventArgs ToStandardEventArgs() + { + switch (Action) + { + case NotifyCollectionChangedAction.Add: + if (IsSingleItem) + { + return new NotifyCollectionChangedEventArgs(Action, NewItem, NewStartingIndex); + } + else + { + return new NotifyCollectionChangedEventArgs(Action, NewItems.ToArray(), NewStartingIndex); + } + case NotifyCollectionChangedAction.Remove: + if (IsSingleItem) + { + return new NotifyCollectionChangedEventArgs(Action, OldItem, OldStartingIndex); + } + else + { + return new NotifyCollectionChangedEventArgs(Action, OldItems.ToArray(), OldStartingIndex); + } + case NotifyCollectionChangedAction.Replace: + if (IsSingleItem) + { + return new NotifyCollectionChangedEventArgs(Action, NewItem, OldItem, NewStartingIndex); + } + else + { + return new NotifyCollectionChangedEventArgs(Action, NewItems.ToArray(), OldItems.ToArray(), NewStartingIndex); + } + case NotifyCollectionChangedAction.Move: + { + return new NotifyCollectionChangedEventArgs(Action, OldItem, NewStartingIndex, OldStartingIndex); + } + case NotifyCollectionChangedAction.Reset: + return new NotifyCollectionChangedEventArgs(Action); + default: + throw new ArgumentOutOfRangeException(); + } + } + + public static NotifyCollectionChangedEventArgs Add(T newItem, int newStartingIndex) + { + return new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Add, true, newItem: newItem, newStartingIndex: newStartingIndex); + } + + public static NotifyCollectionChangedEventArgs Add(ReadOnlySpan newItems, int newStartingIndex) + { + return new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Add, false, newItems: newItems, newStartingIndex: newStartingIndex); + } + + public static NotifyCollectionChangedEventArgs Remove(T oldItem, int oldStartingIndex) + { + return new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Remove, true, oldItem: oldItem, oldStartingIndex: oldStartingIndex); + } + + public static NotifyCollectionChangedEventArgs Remove(ReadOnlySpan oldItems, int oldStartingIndex) + { + return new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Remove, false, oldItems: oldItems, oldStartingIndex: oldStartingIndex); + } + + public static NotifyCollectionChangedEventArgs Replace(T newItem, T oldItem, int startingIndex) + { + return new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Replace, true, newItem: newItem, oldItem: oldItem, newStartingIndex: startingIndex, oldStartingIndex: startingIndex); + } + + public static NotifyCollectionChangedEventArgs Replace(ReadOnlySpan newItems, ReadOnlySpan oldItems, int startingIndex) + { + return new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Replace, false, newItems: newItems, oldItems: oldItems, newStartingIndex: startingIndex, oldStartingIndex: startingIndex); + } + + public static NotifyCollectionChangedEventArgs Move(T changedItem, int newStartingIndex, int oldStartingIndex) + { + return new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Move, true, oldItem: changedItem, newItem: changedItem, newStartingIndex: newStartingIndex, oldStartingIndex: oldStartingIndex); + } + + public static NotifyCollectionChangedEventArgs Reset() + { + return new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Reset, true); + } + } +} \ No newline at end of file diff --git a/src/ObservableCollections.Unity/Assets/Plugins/ObservableCollections/Runtime/NotifyCollectionChangedEventArgs.cs.meta b/src/ObservableCollections.Unity/Assets/Plugins/ObservableCollections/Runtime/NotifyCollectionChangedEventArgs.cs.meta new file mode 100644 index 0000000..e32cd54 --- /dev/null +++ b/src/ObservableCollections.Unity/Assets/Plugins/ObservableCollections/Runtime/NotifyCollectionChangedEventArgs.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 70f96ba473572df4d91f125c2663b868 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/src/ObservableCollections.Unity/Assets/Plugins/ObservableCollections/Runtime/ObservableDictionary.Views.cs b/src/ObservableCollections.Unity/Assets/Plugins/ObservableCollections/Runtime/ObservableDictionary.Views.cs new file mode 100644 index 0000000..0416c1b --- /dev/null +++ b/src/ObservableCollections.Unity/Assets/Plugins/ObservableCollections/Runtime/ObservableDictionary.Views.cs @@ -0,0 +1,174 @@ +using ObservableCollections.Internal; +using System; +using System.Collections; +using System.Collections.Generic; +using System.Collections.Specialized; +using System.Linq; + +namespace ObservableCollections +{ + public sealed partial class ObservableDictionary + { + public ISynchronizedView, TView> CreateView(Func, TView> transform, bool _ = false) + { + // reverse is no used. + return new View(this, transform); + } + + // using key implicitly + public ISynchronizedView, TView> CreateSortedView(Func, TView> transform, IComparer> comparer) + { + return this.CreateSortedView(x => x.Key, transform, comparer); + } + + public ISynchronizedView, TView> CreateSortedView(Func, TView> transform, IComparer viewComparer) + { + return this.CreateSortedView(x => x.Key, transform, viewComparer); + } + + class View : ISynchronizedView, TView> + { + readonly ObservableDictionary source; + readonly Func, TView> selector; + ISynchronizedViewFilter, TView> filter; + readonly Dictionary dict; + + public View(ObservableDictionary source, Func, TView> selector) + { + this.source = source; + this.selector = selector; + this.filter = SynchronizedViewFilter, TView>.Null; + this.SyncRoot = new object(); + lock (source.SyncRoot) + { + this.dict = source.dictionary.ToDictionary(x => x.Key, x => (x.Value, selector(x))); + this.source.CollectionChanged += SourceCollectionChanged; + } + } + + public object SyncRoot { get; } + public event NotifyCollectionChangedEventHandler> RoutingCollectionChanged; + public event Action CollectionStateChanged; + + public int Count + { + get + { + lock (SyncRoot) + { + return dict.Count; + } + } + } + + public void Dispose() + { + this.source.CollectionChanged -= SourceCollectionChanged; + } + + public void AttachFilter(ISynchronizedViewFilter, TView> filter) + { + lock (SyncRoot) + { + this.filter = filter; + foreach (var v in dict) + { + filter.InvokeOnAttach(new KeyValuePair(v.Key, v.Value.Item1), v.Value.Item2); + } + } + } + + public void ResetFilter(Action, TView> resetAction) + { + lock (SyncRoot) + { + this.filter = SynchronizedViewFilter, TView>.Null; + if (resetAction != null) + { + foreach (var v in dict) + { + resetAction(new KeyValuePair(v.Key, v.Value.Item1), v.Value.Item2); + } + } + } + } + + public INotifyCollectionChangedSynchronizedView, TView> WithINotifyCollectionChanged() + { + lock (SyncRoot) + { + return new NotifyCollectionChangedSynchronizedView, TView>(this); + } + } + + public IEnumerator<(KeyValuePair, TView)> GetEnumerator() + { + return new SynchronizedViewEnumerator, TView>(SyncRoot, + dict.Select(x => (new KeyValuePair(x.Key, x.Value.Item1), x.Value.Item2)).GetEnumerator(), + filter); + } + + IEnumerator IEnumerable.GetEnumerator() + { + return GetEnumerator(); + } + + private void SourceCollectionChanged(in NotifyCollectionChangedEventArgs> e) + { + // ObservableDictionary only provides single item operation and does not use int index. + lock (SyncRoot) + { + switch (e.Action) + { + case NotifyCollectionChangedAction.Add: + { + var v = selector(e.NewItem); + dict.Add(e.NewItem.Key, (e.NewItem.Value, v)); + filter.InvokeOnAdd(new KeyValuePair(e.NewItem.Key, e.NewItem.Value), v); + } + break; + case NotifyCollectionChangedAction.Remove: + { + if (dict.Remove(e.OldItem.Key, out var v)) + { + filter.InvokeOnRemove((new KeyValuePair(e.OldItem.Key, v.Item1), v.Item2)); + } + } + break; + case NotifyCollectionChangedAction.Replace: + { + if (dict.Remove(e.OldItem.Key, out var oldView)) + { + filter.InvokeOnRemove((new KeyValuePair(e.OldItem.Key, oldView.Item1), oldView.Item2)); + } + + var v = selector(e.NewItem); + dict[e.NewItem.Key] = (e.NewItem.Value, v); + filter.InvokeOnAdd(new KeyValuePair(e.NewItem.Key, e.NewItem.Value), v); + } + break; + case NotifyCollectionChangedAction.Reset: + { + if (!filter.IsNullFilter()) + { + foreach (var item in dict) + { + filter.InvokeOnRemove((new KeyValuePair(item.Key, item.Value.Item1), item.Value.Item2)); + } + } + + dict.Clear(); + } + break; + case NotifyCollectionChangedAction.Move: // ObservableDictionary have no Move operation. + default: + break; + } + + RoutingCollectionChanged?.Invoke(e); + CollectionStateChanged?.Invoke(e.Action); + } + } + } + } +} diff --git a/src/ObservableCollections.Unity/Assets/Plugins/ObservableCollections/Runtime/ObservableDictionary.Views.cs.meta b/src/ObservableCollections.Unity/Assets/Plugins/ObservableCollections/Runtime/ObservableDictionary.Views.cs.meta new file mode 100644 index 0000000..91e947f --- /dev/null +++ b/src/ObservableCollections.Unity/Assets/Plugins/ObservableCollections/Runtime/ObservableDictionary.Views.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: f512d4ba320de58428976eb614bfa06d +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/src/ObservableCollections.Unity/Assets/Plugins/ObservableCollections/Runtime/ObservableDictionary.cs b/src/ObservableCollections.Unity/Assets/Plugins/ObservableCollections/Runtime/ObservableDictionary.cs new file mode 100644 index 0000000..6740dbd --- /dev/null +++ b/src/ObservableCollections.Unity/Assets/Plugins/ObservableCollections/Runtime/ObservableDictionary.cs @@ -0,0 +1,222 @@ +using ObservableCollections.Internal; +using System; +using System.Collections; +using System.Collections.Generic; +using System.Diagnostics.CodeAnalysis; + +namespace ObservableCollections +{ + public sealed partial class ObservableDictionary + : IDictionary, IReadOnlyDictionary, IObservableCollection> + + { + readonly Dictionary dictionary; + public object SyncRoot { get; } = new object(); + + public ObservableDictionary() + { + this.dictionary = new Dictionary(); + } + + public ObservableDictionary(IEnumerable> collection) + { +#if NET6_0_OR_GREATER + this.dictionary = new Dictionary(collection); +#else + this.dictionary = new Dictionary(); + foreach (var item in collection) + { + dictionary.Add(item.Key, item.Value); + } +#endif + } + + public event NotifyCollectionChangedEventHandler> CollectionChanged; + + public TValue this[TKey key] + { + get + { + lock (SyncRoot) + { + return dictionary[key]; + } + } + set + { + lock (SyncRoot) + { + if (dictionary.TryGetValue(key, out var oldValue)) + { + dictionary[key] = value; + CollectionChanged?.Invoke(NotifyCollectionChangedEventArgs>.Replace( + new KeyValuePair(key, value), + new KeyValuePair(key, oldValue), + -1)); + } + else + { + Add(key, value); + } + } + } + } + + // for lock synchronization, hide keys and values. + ICollection IDictionary.Keys + { + get + { + lock (SyncRoot) + { + return dictionary.Keys; + } + } + } + + ICollection IDictionary.Values + { + get + { + lock (SyncRoot) + { + return dictionary.Values; + } + } + } + + public int Count + { + get + { + lock (SyncRoot) + { + return dictionary.Count; + } + } + } + + public bool IsReadOnly => false; + + IEnumerable IReadOnlyDictionary.Keys + { + get + { + lock (SyncRoot) + { + return dictionary.Keys; + } + } + } + + IEnumerable IReadOnlyDictionary.Values + { + get + { + lock (SyncRoot) + { + return dictionary.Values; + } + } + } + + public void Add(TKey key, TValue value) + { + lock (SyncRoot) + { + dictionary.Add(key, value); + CollectionChanged?.Invoke(NotifyCollectionChangedEventArgs>.Add(new KeyValuePair(key, value), -1)); + } + } + + public void Add(KeyValuePair item) + { + Add(item.Key, item.Value); + } + + public void Clear() + { + lock (SyncRoot) + { + dictionary.Clear(); + CollectionChanged?.Invoke(NotifyCollectionChangedEventArgs>.Reset()); + } + } + + public bool Contains(KeyValuePair item) + { + lock (SyncRoot) + { + return ((ICollection>)dictionary).Contains(item); + } + } + + public bool ContainsKey(TKey key) + { + lock (SyncRoot) + { + return ((IDictionary)dictionary).ContainsKey(key); + } + } + + public void CopyTo(KeyValuePair[] array, int arrayIndex) + { + lock (SyncRoot) + { + ((ICollection>)dictionary).CopyTo(array, arrayIndex); + } + } + + public bool Remove(TKey key) + { + lock (SyncRoot) + { + if (dictionary.Remove(key, out var value)) + { + CollectionChanged?.Invoke(NotifyCollectionChangedEventArgs>.Remove(new KeyValuePair(key, value), -1)); + return true; + } + return false; + } + } + + public bool Remove(KeyValuePair item) + { + lock (SyncRoot) + { + if (dictionary.TryGetValue(item.Key, out var value)) + { + if (EqualityComparer.Default.Equals(value, item.Value)) + { + if (dictionary.Remove(item.Key, out var value2)) + { + CollectionChanged?.Invoke(NotifyCollectionChangedEventArgs>.Remove(new KeyValuePair(item.Key, value2), -1)); + return true; + } + } + } + return false; + } + } + +#pragma warning disable CS8767 + public bool TryGetValue(TKey key, [MaybeNullWhen(false)] out TValue value) +#pragma warning restore CS8767 + { + lock (SyncRoot) + { + return dictionary.TryGetValue(key, out value); + } + } + + public IEnumerator> GetEnumerator() + { + return new SynchronizedEnumerator>(SyncRoot, dictionary.GetEnumerator()); + } + + IEnumerator IEnumerable.GetEnumerator() + { + return GetEnumerator(); + } + } +} diff --git a/src/ObservableCollections.Unity/Assets/Plugins/ObservableCollections/Runtime/ObservableDictionary.cs.meta b/src/ObservableCollections.Unity/Assets/Plugins/ObservableCollections/Runtime/ObservableDictionary.cs.meta new file mode 100644 index 0000000..527d0cc --- /dev/null +++ b/src/ObservableCollections.Unity/Assets/Plugins/ObservableCollections/Runtime/ObservableDictionary.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: b4dc0886473cf8c49947c9081454c614 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/src/ObservableCollections.Unity/Assets/Plugins/ObservableCollections/Runtime/ObservableFixedSizeRingBuffer.cs b/src/ObservableCollections.Unity/Assets/Plugins/ObservableCollections/Runtime/ObservableFixedSizeRingBuffer.cs new file mode 100644 index 0000000..f696896 --- /dev/null +++ b/src/ObservableCollections.Unity/Assets/Plugins/ObservableCollections/Runtime/ObservableFixedSizeRingBuffer.cs @@ -0,0 +1,296 @@ +using ObservableCollections.Internal; +using System; +using System.Collections; +using System.Collections.Generic; + +namespace ObservableCollections +{ + public sealed partial class ObservableFixedSizeRingBuffer : IList, IReadOnlyList, IObservableCollection + { + readonly RingBuffer buffer; + readonly int capacity; + + public event NotifyCollectionChangedEventHandler CollectionChanged; + + public ObservableFixedSizeRingBuffer(int capacity) + { + this.capacity = capacity; + this.buffer = new RingBuffer(capacity); + } + + public ObservableFixedSizeRingBuffer(int capacity, IEnumerable collection) + { + this.capacity = capacity; + this.buffer = new RingBuffer(capacity); + foreach (var item in collection) + { + if (capacity == buffer.Count) + { + buffer.RemoveFirst(); + } + buffer.AddLast(item); + } + } + + public bool IsReadOnly => false; + + public object SyncRoot { get; } = new object(); + + public T this[int index] + { + get + { + lock (SyncRoot) + { + return this.buffer[index]; + } + } + set + { + lock (SyncRoot) + { + var oldValue = buffer[index]; + buffer[index] = value; + CollectionChanged?.Invoke(NotifyCollectionChangedEventArgs.Replace(value, oldValue, index)); + } + } + } + + public int Count + { + get + { + lock (SyncRoot) + { + return buffer.Count; + } + } + } + + public void AddFirst(T item) + { + lock (SyncRoot) + { + if (capacity == buffer.Count) + { + var remItem = buffer.RemoveLast(); + CollectionChanged?.Invoke(NotifyCollectionChangedEventArgs.Remove(remItem, capacity - 1)); + } + + buffer.AddFirst(item); + CollectionChanged?.Invoke(NotifyCollectionChangedEventArgs.Add(item, 0)); + } + } + + public void AddLast(T item) + { + lock (SyncRoot) + { + if (capacity == buffer.Count) + { + var remItem = buffer.RemoveFirst(); + CollectionChanged?.Invoke(NotifyCollectionChangedEventArgs.Remove(remItem, 0)); + } + + buffer.AddLast(item); + CollectionChanged?.Invoke(NotifyCollectionChangedEventArgs.Add(item, buffer.Count - 1)); + } + } + + public T RemoveFirst() + { + lock (SyncRoot) + { + var item = buffer.RemoveFirst(); + CollectionChanged?.Invoke(NotifyCollectionChangedEventArgs.Remove(item, 0)); + return item; + } + } + + public T RemoveLast() + { + lock (SyncRoot) + { + var index = buffer.Count - 1; + var item = buffer.RemoveLast(); + CollectionChanged?.Invoke(NotifyCollectionChangedEventArgs.Remove(item, index)); + return item; + } + } + + // AddFirstRange is not exists. + + public void AddLastRange(IEnumerable items) + { + lock (SyncRoot) + { + using (var xs = new CloneCollection(items)) + { + if (capacity <= buffer.Count + xs.Span.Length) + { + // calc remove count + var remCount = Math.Min(buffer.Count, buffer.Count + xs.Span.Length - capacity); + using (var ys = new ResizableArray(remCount)) + { + for (int i = 0; i < remCount; i++) + { + ys.Add(buffer.RemoveFirst()); + } + + CollectionChanged?.Invoke(NotifyCollectionChangedEventArgs.Remove(ys.Span, 0)); + } + } + + var index = buffer.Count; + var span = xs.Span; + if (span.Length > capacity) + { + span = span.Slice(span.Length - capacity); + } + + foreach (var item in span) + { + buffer.AddLast(item); + } + CollectionChanged?.Invoke(NotifyCollectionChangedEventArgs.Add(span, index)); + } + } + } + + public void AddLastRange(T[] items) + { + lock (SyncRoot) + { + if (capacity <= buffer.Count + items.Length) + { + // calc remove count + var remCount = Math.Min(buffer.Count, buffer.Count + items.Length - capacity); + using (var ys = new ResizableArray(remCount)) + { + for (int i = 0; i < remCount; i++) + { + ys.Add(buffer.RemoveFirst()); + } + + CollectionChanged?.Invoke(NotifyCollectionChangedEventArgs.Remove(ys.Span, 0)); + } + } + + var index = buffer.Count; + var span = items.AsSpan(); + if (span.Length > capacity) + { + span = span.Slice(span.Length - capacity); + } + + foreach (var item in span) + { + buffer.AddLast(item); + } + CollectionChanged?.Invoke(NotifyCollectionChangedEventArgs.Add(span, index)); + } + } + + public void AddLastRange(ReadOnlySpan items) + { + lock (SyncRoot) + { + if (capacity <= buffer.Count + items.Length) + { + // calc remove count + var remCount = Math.Min(buffer.Count, buffer.Count + items.Length - capacity); + using (var ys = new ResizableArray(remCount)) + { + for (int i = 0; i < remCount; i++) + { + ys.Add(buffer.RemoveFirst()); + } + + CollectionChanged?.Invoke(NotifyCollectionChangedEventArgs.Remove(ys.Span, 0)); + } + } + + var index = buffer.Count; + var span = items; + if (span.Length > capacity) + { + span = span.Slice(span.Length - capacity); + } + + foreach (var item in span) + { + buffer.AddLast(item); + } + CollectionChanged?.Invoke(NotifyCollectionChangedEventArgs.Add(span, index)); + } + } + + public int IndexOf(T item) + { + lock (SyncRoot) + { + return buffer.IndexOf(item); + } + } + + void IList.Insert(int index, T item) + { + throw new NotSupportedException(); + } + + bool ICollection.Remove(T item) + { + throw new NotSupportedException(); + } + + void IList.RemoveAt(int index) + { + throw new NotSupportedException(); + } + + void ICollection.Add(T item) + { + AddLast(item); + } + + public void Clear() + { + lock (SyncRoot) + { + buffer.Clear(); + CollectionChanged?.Invoke(NotifyCollectionChangedEventArgs.Reset()); + } + } + + public bool Contains(T item) + { + lock (SyncRoot) + { + return buffer.Contains(item); + } + } + + public void CopyTo(T[] array, int arrayIndex) + { + lock (SyncRoot) + { + buffer.CopyTo(array, arrayIndex); + } + } + + public IEnumerator GetEnumerator() + { + return new SynchronizedEnumerator(SyncRoot, buffer.GetEnumerator()); + } + + IEnumerator IEnumerable.GetEnumerator() + { + return GetEnumerator(); + } + + public ISynchronizedView CreateView(Func transform, bool reverse = false) + { + return new ObservableRingBuffer.View(this, transform, reverse); + } + } +} diff --git a/src/ObservableCollections.Unity/Assets/Plugins/ObservableCollections/Runtime/ObservableFixedSizeRingBuffer.cs.meta b/src/ObservableCollections.Unity/Assets/Plugins/ObservableCollections/Runtime/ObservableFixedSizeRingBuffer.cs.meta new file mode 100644 index 0000000..6de88c3 --- /dev/null +++ b/src/ObservableCollections.Unity/Assets/Plugins/ObservableCollections/Runtime/ObservableFixedSizeRingBuffer.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 81d799cc7eb02954d89c7ee52b7c9148 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/src/ObservableCollections.Unity/Assets/Plugins/ObservableCollections/Runtime/ObservableHashSet.Views.cs b/src/ObservableCollections.Unity/Assets/Plugins/ObservableCollections/Runtime/ObservableHashSet.Views.cs new file mode 100644 index 0000000..c6448dc --- /dev/null +++ b/src/ObservableCollections.Unity/Assets/Plugins/ObservableCollections/Runtime/ObservableHashSet.Views.cs @@ -0,0 +1,165 @@ +using ObservableCollections.Internal; +using System.Collections; +using System.Collections.Generic; +using System.Collections.Specialized; +using System; +using System.Linq; + +namespace ObservableCollections +{ + public sealed partial class ObservableHashSet : IReadOnlyCollection, IObservableCollection + { + public ISynchronizedView CreateView(Func transform, bool _ = false) + { + return new View(this, transform); + } + + sealed class View : ISynchronizedView + { + readonly ObservableHashSet source; + readonly Func selector; + readonly Dictionary dict; + + ISynchronizedViewFilter filter; + + public event NotifyCollectionChangedEventHandler RoutingCollectionChanged; + public event Action CollectionStateChanged; + + public object SyncRoot { get; } + + public View(ObservableHashSet source, Func selector) + { + this.source = source; + this.selector = selector; + this.filter = SynchronizedViewFilter.Null; + this.SyncRoot = new object(); + lock (source.SyncRoot) + { + this.dict = source.set.ToDictionary(x => x, x => (x, selector(x))); + this.source.CollectionChanged += SourceCollectionChanged; + } + } + + public int Count + { + get + { + lock (SyncRoot) + { + return dict.Count; + } + } + } + + public void AttachFilter(ISynchronizedViewFilter filter) + { + lock (SyncRoot) + { + this.filter = filter; + foreach (var (_, (value, view)) in dict) + { + filter.InvokeOnAttach(value, view); + } + } + } + + public void ResetFilter(Action resetAction) + { + lock (SyncRoot) + { + this.filter = SynchronizedViewFilter.Null; + if (resetAction != null) + { + foreach (var (_, (value, view)) in dict) + { + resetAction(value, view); + } + } + } + } + + public INotifyCollectionChangedSynchronizedView WithINotifyCollectionChanged() + { + lock (SyncRoot) + { + return new NotifyCollectionChangedSynchronizedView(this); + } + } + + public IEnumerator<(T, TView)> GetEnumerator() + { + return new SynchronizedViewEnumerator(SyncRoot, dict.Select(x => x.Value).GetEnumerator(), filter); + } + + IEnumerator IEnumerable.GetEnumerator() => GetEnumerator(); + + public void Dispose() + { + this.source.CollectionChanged -= SourceCollectionChanged; + } + + private void SourceCollectionChanged(in NotifyCollectionChangedEventArgs e) + { + lock (SyncRoot) + { + switch (e.Action) + { + case NotifyCollectionChangedAction.Add: + if (e.IsSingleItem) + { + var v = (e.NewItem, selector(e.NewItem)); + dict.Add(e.NewItem, v); + filter.InvokeOnAdd(v); + } + else + { + foreach (var item in e.NewItems) + { + var v = (item, selector(item)); + dict.Add(item, v); + filter.InvokeOnAdd(v); + } + } + break; + case NotifyCollectionChangedAction.Remove: + if (e.IsSingleItem) + { + if (dict.Remove(e.OldItem, out var value)) + { + filter.InvokeOnRemove(value.Item1, value.Item2); + } + } + else + { + foreach (var item in e.OldItems) + { + if (dict.Remove(item, out var value)) + { + filter.InvokeOnRemove(value.Item1, value.Item2); + } + } + } + break; + case NotifyCollectionChangedAction.Reset: + if (!filter.IsNullFilter()) + { + foreach (var item in dict) + { + filter.InvokeOnRemove(item.Value); + } + } + dict.Clear(); + break; + case NotifyCollectionChangedAction.Replace: + case NotifyCollectionChangedAction.Move: + default: + break; + } + + RoutingCollectionChanged?.Invoke(e); + CollectionStateChanged?.Invoke(e.Action); + } + } + } + } +} diff --git a/src/ObservableCollections.Unity/Assets/Plugins/ObservableCollections/Runtime/ObservableHashSet.Views.cs.meta b/src/ObservableCollections.Unity/Assets/Plugins/ObservableCollections/Runtime/ObservableHashSet.Views.cs.meta new file mode 100644 index 0000000..7656202 --- /dev/null +++ b/src/ObservableCollections.Unity/Assets/Plugins/ObservableCollections/Runtime/ObservableHashSet.Views.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: a7b9e5ab06e68b242a4a722866654fd7 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/src/ObservableCollections.Unity/Assets/Plugins/ObservableCollections/Runtime/ObservableHashSet.cs b/src/ObservableCollections.Unity/Assets/Plugins/ObservableCollections/Runtime/ObservableHashSet.cs new file mode 100644 index 0000000..25a78b2 --- /dev/null +++ b/src/ObservableCollections.Unity/Assets/Plugins/ObservableCollections/Runtime/ObservableHashSet.cs @@ -0,0 +1,258 @@ +using ObservableCollections.Internal; +using System; +using System.Collections; +using System.Collections.Generic; +using System.Diagnostics.CodeAnalysis; + +namespace ObservableCollections +{ + // can not implements ISet because set operation can not get added/removed values. + public sealed partial class ObservableHashSet : IReadOnlySet, IReadOnlyCollection, IObservableCollection + + { + readonly HashSet set; + public object SyncRoot { get; } = new object(); + + public ObservableHashSet() + { + this.set = new HashSet(); + } + +#if NETSTANDARD2_1_OR_GREATER || NET5_0_OR_GREATER + + public ObservableHashSet(int capacity) + { + this.set = new HashSet(capacity); + } + +#endif + + public ObservableHashSet(IEnumerable collection) + { + this.set = new HashSet(collection); + } + + public event NotifyCollectionChangedEventHandler CollectionChanged; + + public int Count + { + get + { + lock (SyncRoot) + { + return set.Count; + } + } + } + + public bool IsReadOnly => false; + + public bool Add(T item) + { + lock (SyncRoot) + { + if (set.Add(item)) + { + CollectionChanged?.Invoke(NotifyCollectionChangedEventArgs.Add(item, -1)); + return true; + } + + return false; + } + } + + public void AddRange(IEnumerable items) + { + lock (SyncRoot) + { + if (!items.TryGetNonEnumeratedCount(out var capacity)) + { + capacity = 4; + } + + using (var list = new ResizableArray(capacity)) + { + foreach (var item in items) + { + if (set.Add(item)) + { + list.Add(item); + } + } + + CollectionChanged?.Invoke(NotifyCollectionChangedEventArgs.Add(list.Span, -1)); + } + } + } + + public void AddRange(T[] items) + { + AddRange(items.AsSpan()); + } + + public void AddRange(ReadOnlySpan items) + { + lock (SyncRoot) + { + using (var list = new ResizableArray(items.Length)) + { + foreach (var item in items) + { + if (set.Add(item)) + { + list.Add(item); + } + } + + CollectionChanged?.Invoke(NotifyCollectionChangedEventArgs.Add(list.Span, -1)); + } + } + } + + public bool Remove(T item) + { + lock (SyncRoot) + { + if (set.Remove(item)) + { + CollectionChanged?.Invoke(NotifyCollectionChangedEventArgs.Remove(item, -1)); + return true; + } + + return false; + } + } + + public void RemoveRange(IEnumerable items) + { + lock (SyncRoot) + { + if (!items.TryGetNonEnumeratedCount(out var capacity)) + { + capacity = 4; + } + + using (var list = new ResizableArray(capacity)) + { + foreach (var item in items) + { + if (set.Remove(item)) + { + list.Add(item); + } + } + + CollectionChanged?.Invoke(NotifyCollectionChangedEventArgs.Remove(list.Span, -1)); + } + } + } + + public void RemoveRange(T[] items) + { + RemoveRange(items.AsSpan()); + } + + public void RemoveRange(ReadOnlySpan items) + { + lock (SyncRoot) + { + using (var list = new ResizableArray(items.Length)) + { + foreach (var item in items) + { + if (set.Remove(item)) + { + list.Add(item); + } + } + + CollectionChanged?.Invoke(NotifyCollectionChangedEventArgs.Remove(list.Span, -1)); + } + } + } + + public void Clear() + { + lock (SyncRoot) + { + set.Clear(); + CollectionChanged?.Invoke(NotifyCollectionChangedEventArgs.Reset()); + } + } + +#if !NETSTANDARD2_0 && !NET_STANDARD_2_0 && !NET_4_6 + + public bool TryGetValue(T equalValue, [MaybeNullWhen(false)] out T actualValue) + { + return set.TryGetValue(equalValue, out actualValue); + } + +#endif + + public bool Contains(T item) + { + lock (SyncRoot) + { + return set.Contains(item); + } + } + + public bool IsProperSubsetOf(IEnumerable other) + { + lock (SyncRoot) + { + return set.IsProperSubsetOf(other); + } + } + + public bool IsProperSupersetOf(IEnumerable other) + { + lock (SyncRoot) + { + return set.IsProperSupersetOf(other); + } + } + + public bool IsSubsetOf(IEnumerable other) + { + lock (SyncRoot) + { + return set.IsSubsetOf(other); + } + } + + public bool IsSupersetOf(IEnumerable other) + { + lock (SyncRoot) + { + return set.IsSupersetOf(other); + } + } + + public bool Overlaps(IEnumerable other) + { + lock (SyncRoot) + { + return set.Overlaps(other); + } + } + + public bool SetEquals(IEnumerable other) + { + lock (SyncRoot) + { + return set.SetEquals(other); + } + } + + public IEnumerator GetEnumerator() + { + return new SynchronizedEnumerator(SyncRoot, set.GetEnumerator()); + } + + IEnumerator IEnumerable.GetEnumerator() + { + return GetEnumerator(); + } + } +} diff --git a/src/ObservableCollections.Unity/Assets/Plugins/ObservableCollections/Runtime/ObservableHashSet.cs.meta b/src/ObservableCollections.Unity/Assets/Plugins/ObservableCollections/Runtime/ObservableHashSet.cs.meta new file mode 100644 index 0000000..5f62f85 --- /dev/null +++ b/src/ObservableCollections.Unity/Assets/Plugins/ObservableCollections/Runtime/ObservableHashSet.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 38d982a9907bb0047b158794a1cf35e7 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/src/ObservableCollections.Unity/Assets/Plugins/ObservableCollections/Runtime/ObservableList.Views.cs b/src/ObservableCollections.Unity/Assets/Plugins/ObservableCollections/Runtime/ObservableList.Views.cs new file mode 100644 index 0000000..5f46579 --- /dev/null +++ b/src/ObservableCollections.Unity/Assets/Plugins/ObservableCollections/Runtime/ObservableList.Views.cs @@ -0,0 +1,220 @@ +using ObservableCollections.Internal; +using System; +using System.Collections; +using System.Collections.Generic; +using System.Collections.Specialized; +using System.Linq; + +namespace ObservableCollections +{ + public sealed partial class ObservableList : IList, IReadOnlyList, IObservableCollection + { + public ISynchronizedView CreateView(Func transform, bool reverse = false) + { + return new View(this, transform, reverse); + } + + sealed class View : ISynchronizedView + { + readonly ObservableList source; + readonly Func selector; + readonly bool reverse; + readonly List<(T, TView)> list; + + ISynchronizedViewFilter filter; + + public event NotifyCollectionChangedEventHandler RoutingCollectionChanged; + public event Action CollectionStateChanged; + + public object SyncRoot { get; } + + public View(ObservableList source, Func selector, bool reverse) + { + this.source = source; + this.selector = selector; + this.reverse = reverse; + this.filter = SynchronizedViewFilter.Null; + this.SyncRoot = new object(); + lock (source.SyncRoot) + { + this.list = source.list.Select(x => (x, selector(x))).ToList(); + this.source.CollectionChanged += SourceCollectionChanged; + } + } + + public int Count + { + get + { + lock (SyncRoot) + { + return list.Count; + } + } + } + + public void AttachFilter(ISynchronizedViewFilter filter) + { + lock (SyncRoot) + { + this.filter = filter; + foreach (var (value, view) in list) + { + filter.InvokeOnAttach(value, view); + } + } + } + + public void ResetFilter(Action resetAction) + { + lock (SyncRoot) + { + this.filter = SynchronizedViewFilter.Null; + if (resetAction != null) + { + foreach (var (item, view) in list) + { + resetAction(item, view); + } + } + } + } + + public INotifyCollectionChangedSynchronizedView WithINotifyCollectionChanged() + { + lock (SyncRoot) + { + return new NotifyCollectionChangedSynchronizedView(this); + } + } + + public IEnumerator<(T, TView)> GetEnumerator() + { + if (!reverse) + { + return new SynchronizedViewEnumerator(SyncRoot, list.GetEnumerator(), filter); + } + else + { + return new SynchronizedViewEnumerator(SyncRoot, list.AsEnumerable().Reverse().GetEnumerator(), filter); + } + } + + IEnumerator IEnumerable.GetEnumerator() => GetEnumerator(); + + public void Dispose() + { + this.source.CollectionChanged -= SourceCollectionChanged; + } + + private void SourceCollectionChanged(in NotifyCollectionChangedEventArgs e) + { + lock (SyncRoot) + { + switch (e.Action) + { + case NotifyCollectionChangedAction.Add: + // Add + if (e.NewStartingIndex == list.Count) + { + if (e.IsSingleItem) + { + var v = (e.NewItem, selector(e.NewItem)); + list.Add(v); + filter.InvokeOnAdd(v); + } + else + { + foreach (var item in e.NewItems) + { + var v = (item, selector(item)); + list.Add(v); + filter.InvokeOnAdd(v); + } + } + } + // Insert + else + { + if (e.IsSingleItem) + { + var v = (e.NewItem, selector(e.NewItem)); + list.Insert(e.NewStartingIndex, v); + filter.InvokeOnAdd(v); + } + else + { + // inefficient copy, need refactoring + var newArray = new (T, TView)[e.NewItems.Length]; + var span = e.NewItems; + for (int i = 0; i < span.Length; i++) + { + var v = (span[i], selector(span[i])); + newArray[i] = v; + filter.InvokeOnAdd(v); + } + list.InsertRange(e.NewStartingIndex, newArray); + } + } + break; + case NotifyCollectionChangedAction.Remove: + if (e.IsSingleItem) + { + var v = list[e.OldStartingIndex]; + list.RemoveAt(e.OldStartingIndex); + filter.InvokeOnRemove(v.Item1, v.Item2); + } + else + { + var len = e.OldStartingIndex + e.OldItems.Length; + for (int i = e.OldStartingIndex; i < len; i++) + { + var v = list[i]; + filter.InvokeOnRemove(v.Item1, v.Item2); + } + + list.RemoveRange(e.OldStartingIndex, e.OldItems.Length); + } + break; + case NotifyCollectionChangedAction.Replace: + // ObservableList does not support replace range + { + var v = (e.NewItem, selector(e.NewItem)); + + var oldItem = list[e.NewStartingIndex]; + list[e.NewStartingIndex] = v; + + filter.InvokeOnRemove(oldItem); + filter.InvokeOnAdd(v); + break; + } + case NotifyCollectionChangedAction.Move: + { + var removeItem = list[e.OldStartingIndex]; + list.RemoveAt(e.OldStartingIndex); + list.Insert(e.NewStartingIndex, removeItem); + + filter.InvokeOnMove(removeItem); + } + break; + case NotifyCollectionChangedAction.Reset: + if (!filter.IsNullFilter()) + { + foreach (var item in list) + { + filter.InvokeOnRemove(item); + } + } + list.Clear(); + break; + default: + break; + } + + RoutingCollectionChanged?.Invoke(e); + CollectionStateChanged?.Invoke(e.Action); + } + } + } + } +} \ No newline at end of file diff --git a/src/ObservableCollections.Unity/Assets/Plugins/ObservableCollections/Runtime/ObservableList.Views.cs.meta b/src/ObservableCollections.Unity/Assets/Plugins/ObservableCollections/Runtime/ObservableList.Views.cs.meta new file mode 100644 index 0000000..1d244cc --- /dev/null +++ b/src/ObservableCollections.Unity/Assets/Plugins/ObservableCollections/Runtime/ObservableList.Views.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: b98cf9d4d5426ad478ede70c5ac637d2 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/src/ObservableCollections.Unity/Assets/Plugins/ObservableCollections/Runtime/ObservableList.cs b/src/ObservableCollections.Unity/Assets/Plugins/ObservableCollections/Runtime/ObservableList.cs new file mode 100644 index 0000000..d3e6ba5 --- /dev/null +++ b/src/ObservableCollections.Unity/Assets/Plugins/ObservableCollections/Runtime/ObservableList.cs @@ -0,0 +1,270 @@ +using ObservableCollections.Internal; +using System; +using System.Collections; +using System.Collections.Generic; +using System.Linq; +using System.Runtime.InteropServices; + +namespace ObservableCollections +{ + public sealed partial class ObservableList : IList, IReadOnlyList, IObservableCollection + { + readonly List list; + public object SyncRoot { get; } = new object(); + + public ObservableList() + { + list = new List(); + } + + public ObservableList(int capacity) + { + list = new List(capacity); + } + + public ObservableList(IEnumerable collection) + { + list = collection.ToList(); + } + + public T this[int index] + { + get + { + lock (SyncRoot) + { + return list[index]; + } + } + set + { + lock (SyncRoot) + { + var oldValue = list[index]; + list[index] = value; + CollectionChanged?.Invoke(NotifyCollectionChangedEventArgs.Replace(value, oldValue, index)); + } + } + } + + public int Count + { + get + { + lock (SyncRoot) + { + return list.Count; + } + } + } + + public bool IsReadOnly => false; + + public event NotifyCollectionChangedEventHandler CollectionChanged; + + + public void Add(T item) + { + lock (SyncRoot) + { + var index = list.Count; + list.Add(item); + CollectionChanged?.Invoke(NotifyCollectionChangedEventArgs.Add(item, index)); + } + } + + public void AddRange(IEnumerable items) + { + lock (SyncRoot) + { + var index = list.Count; + using (var xs = new CloneCollection(items)) + { + // to avoid iterate twice, require copy before insert. + list.AddRange(xs.AsEnumerable()); + CollectionChanged?.Invoke(NotifyCollectionChangedEventArgs.Add(xs.Span, index)); + } + } + } + + public void AddRange(T[] items) + { + lock (SyncRoot) + { + var index = list.Count; + list.AddRange(items); + CollectionChanged?.Invoke(NotifyCollectionChangedEventArgs.Add(items, index)); + } + } + + public void AddRange(ReadOnlySpan items) + { + lock (SyncRoot) + { + var index = list.Count; + foreach (var item in items) + { + list.Add(item); + } + + CollectionChanged?.Invoke(NotifyCollectionChangedEventArgs.Add(items, index)); + } + } + + public void Clear() + { + var l = new List(); + lock (SyncRoot) + { + list.Clear(); + CollectionChanged?.Invoke(NotifyCollectionChangedEventArgs.Reset()); + } + } + + public bool Contains(T item) + { + lock (SyncRoot) + { + return list.Contains(item); + } + } + + public void CopyTo(T[] array, int arrayIndex) + { + lock (SyncRoot) + { + list.CopyTo(array, arrayIndex); + } + } + + public IEnumerator GetEnumerator() + { + return new SynchronizedEnumerator(SyncRoot, list.GetEnumerator()); + } + + IEnumerator IEnumerable.GetEnumerator() + { + return GetEnumerator(); + } + + public void ForEach(Action action) + { + lock (SyncRoot) + { + foreach (var item in list) + { + action(item); + } + } + } + + public int IndexOf(T item) + { + lock (SyncRoot) + { + return list.IndexOf(item); + } + } + + public void Insert(int index, T item) + { + lock (SyncRoot) + { + list.Insert(index, item); + CollectionChanged?.Invoke(NotifyCollectionChangedEventArgs.Add(item, index)); + } + } + + public void InsertRange(int index, T[] items) + { + lock (SyncRoot) + { + list.InsertRange(index, items); + CollectionChanged?.Invoke(NotifyCollectionChangedEventArgs.Add(items, index)); + } + } + + public void InsertRange(int index, IEnumerable items) + { + lock (SyncRoot) + { + using (var xs = new CloneCollection(items)) + { + list.InsertRange(index, xs.AsEnumerable()); + CollectionChanged?.Invoke(NotifyCollectionChangedEventArgs.Add(xs.Span, index)); + } + } + } + + public void InsertRange(int index, ReadOnlySpan items) + { + lock (SyncRoot) + { + using (var xs = new CloneCollection(items)) + { + list.InsertRange(index, xs.AsEnumerable()); + CollectionChanged?.Invoke(NotifyCollectionChangedEventArgs.Add(xs.Span, index)); + } + } + } + + public bool Remove(T item) + { + lock (SyncRoot) + { + var index = list.IndexOf(item); + + if (index >= 0) + { + list.RemoveAt(index); + CollectionChanged?.Invoke(NotifyCollectionChangedEventArgs.Remove(item, index)); + return true; + } + else + { + return false; + } + } + } + + public void RemoveAt(int index) + { + lock (SyncRoot) + { + var item = list[index]; + list.RemoveAt(index); + CollectionChanged?.Invoke(NotifyCollectionChangedEventArgs.Remove(item, index)); + } + } + + public void RemoveRange(int index, int count) + { + lock (SyncRoot) + { +#if NET5_0_OR_GREATER + var range = CollectionsMarshal.AsSpan(list).Slice(index, count); +#else + var range = list.GetRange(index, count); +#endif + + // require copy before remove + using (var xs = new CloneCollection(range)) + { + list.RemoveRange(index, count); + CollectionChanged?.Invoke(NotifyCollectionChangedEventArgs.Remove(xs.Span, index)); + } + } + } + + public void Move(int oldIndex, int newIndex) + { + lock (SyncRoot) + { + var removedItem = list[oldIndex]; + list.RemoveAt(oldIndex); + list.Insert(newIndex, removedItem); + CollectionChanged?.Invoke(NotifyCollectionChangedEventArgs.Move(removedItem, newIndex, oldIndex)); + } + } + } +} \ No newline at end of file diff --git a/src/ObservableCollections.Unity/Assets/Plugins/ObservableCollections/Runtime/ObservableList.cs.meta b/src/ObservableCollections.Unity/Assets/Plugins/ObservableCollections/Runtime/ObservableList.cs.meta new file mode 100644 index 0000000..a6e0add --- /dev/null +++ b/src/ObservableCollections.Unity/Assets/Plugins/ObservableCollections/Runtime/ObservableList.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 963681d5debafe6448e09cd9a4029cec +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/src/ObservableCollections.Unity/Assets/Plugins/ObservableCollections/Runtime/ObservableQueue.Views.cs b/src/ObservableCollections.Unity/Assets/Plugins/ObservableCollections/Runtime/ObservableQueue.Views.cs new file mode 100644 index 0000000..d1c5aa8 --- /dev/null +++ b/src/ObservableCollections.Unity/Assets/Plugins/ObservableCollections/Runtime/ObservableQueue.Views.cs @@ -0,0 +1,173 @@ +using ObservableCollections.Internal; +using System.Collections; +using System.Collections.Specialized; +using System; +using System.Collections.Generic; +using System.Linq; + +namespace ObservableCollections +{ + public sealed partial class ObservableQueue : IReadOnlyCollection, IObservableCollection + { + public ISynchronizedView CreateView(Func transform, bool reverse = false) + { + return new View(this, transform, reverse); + } + + class View : ISynchronizedView + { + readonly ObservableQueue source; + readonly Func selector; + readonly bool reverse; + protected readonly Queue<(T, TView)> queue; + + ISynchronizedViewFilter filter; + + public event NotifyCollectionChangedEventHandler RoutingCollectionChanged; + public event Action CollectionStateChanged; + + public object SyncRoot { get; } + + public View(ObservableQueue source, Func selector, bool reverse) + { + this.source = source; + this.selector = selector; + this.reverse = reverse; + this.filter = SynchronizedViewFilter.Null; + this.SyncRoot = new object(); + lock (source.SyncRoot) + { + this.queue = new Queue<(T, TView)>(source.queue.Select(x => (x, selector(x)))); + this.source.CollectionChanged += SourceCollectionChanged; + } + } + + public int Count + { + get + { + lock (SyncRoot) + { + return queue.Count; + } + } + } + + public void AttachFilter(ISynchronizedViewFilter filter) + { + lock (SyncRoot) + { + this.filter = filter; + foreach (var (value, view) in queue) + { + filter.InvokeOnAttach(value, view); + } + } + } + + public void ResetFilter(Action resetAction) + { + lock (SyncRoot) + { + this.filter = SynchronizedViewFilter.Null; + if (resetAction != null) + { + foreach (var (item, view) in queue) + { + resetAction(item, view); + } + } + } + } + + public INotifyCollectionChangedSynchronizedView WithINotifyCollectionChanged() + { + lock (SyncRoot) + { + return new NotifyCollectionChangedSynchronizedView(this); + } + } + + public IEnumerator<(T, TView)> GetEnumerator() + { + if (!reverse) + { + return new SynchronizedViewEnumerator(SyncRoot, queue.GetEnumerator(), filter); + } + else + { + return new SynchronizedViewEnumerator(SyncRoot, queue.AsEnumerable().Reverse().GetEnumerator(), filter); + } + } + + IEnumerator IEnumerable.GetEnumerator() => GetEnumerator(); + + public void Dispose() + { + this.source.CollectionChanged -= SourceCollectionChanged; + } + + private void SourceCollectionChanged(in NotifyCollectionChangedEventArgs e) + { + lock (SyncRoot) + { + switch (e.Action) + { + case NotifyCollectionChangedAction.Add: + // Add(Enqueue, EnqueueRange) + if (e.IsSingleItem) + { + var v = (e.NewItem, selector(e.NewItem)); + queue.Enqueue(v); + filter.InvokeOnAdd(v); + } + else + { + foreach (var item in e.NewItems) + { + var v = (item, selector(item)); + queue.Enqueue(v); + filter.InvokeOnAdd(v); + } + } + break; + case NotifyCollectionChangedAction.Remove: + // Dequeue, DequeuRange + if (e.IsSingleItem) + { + var v = queue.Dequeue(); + filter.InvokeOnRemove(v.Item1, v.Item2); + } + else + { + var len = e.OldItems.Length; + for (int i = 0; i < len; i++) + { + var v = queue.Dequeue(); + filter.InvokeOnRemove(v.Item1, v.Item2); + } + } + break; + case NotifyCollectionChangedAction.Reset: + if (!filter.IsNullFilter()) + { + foreach (var item in queue) + { + filter.InvokeOnRemove(item); + } + } + queue.Clear(); + break; + case NotifyCollectionChangedAction.Replace: + case NotifyCollectionChangedAction.Move: + default: + break; + } + + RoutingCollectionChanged?.Invoke(e); + CollectionStateChanged?.Invoke(e.Action); + } + } + } + } +} \ No newline at end of file diff --git a/src/ObservableCollections.Unity/Assets/Plugins/ObservableCollections/Runtime/ObservableQueue.Views.cs.meta b/src/ObservableCollections.Unity/Assets/Plugins/ObservableCollections/Runtime/ObservableQueue.Views.cs.meta new file mode 100644 index 0000000..072509d --- /dev/null +++ b/src/ObservableCollections.Unity/Assets/Plugins/ObservableCollections/Runtime/ObservableQueue.Views.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 3fea76167e2a69749931bf01b5b5ed92 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/src/ObservableCollections.Unity/Assets/Plugins/ObservableCollections/Runtime/ObservableQueue.cs b/src/ObservableCollections.Unity/Assets/Plugins/ObservableCollections/Runtime/ObservableQueue.cs new file mode 100644 index 0000000..010078e --- /dev/null +++ b/src/ObservableCollections.Unity/Assets/Plugins/ObservableCollections/Runtime/ObservableQueue.cs @@ -0,0 +1,214 @@ +using ObservableCollections.Internal; +using System.Buffers; +using System.Collections; +using System.Diagnostics.CodeAnalysis; +using System.Runtime.CompilerServices; +using System; +using System.Collections.Generic; +using System.Collections.Specialized; +using System.Linq; + +namespace ObservableCollections +{ + public sealed partial class ObservableQueue : IReadOnlyCollection, IObservableCollection + { + readonly Queue queue; + public object SyncRoot { get; } = new object(); + + public ObservableQueue() + { + this.queue = new Queue(); + } + + public ObservableQueue(int capacity) + { + this.queue = new Queue(capacity); + } + + public ObservableQueue(IEnumerable collection) + { + this.queue = new Queue(collection); + } + + public event NotifyCollectionChangedEventHandler CollectionChanged; + + public int Count + { + get + { + lock (SyncRoot) + { + return queue.Count; + } + } + } + + public void Enqueue(T item) + { + lock (SyncRoot) + { + var index = queue.Count; + queue.Enqueue(item); + CollectionChanged?.Invoke(NotifyCollectionChangedEventArgs.Add(item, index)); + } + } + + public void EnqueueRange(IEnumerable items) + { + lock (SyncRoot) + { + var index = queue.Count; + using (var xs = new CloneCollection(items)) + { + foreach (var item in xs.Span) + { + queue.Enqueue(item); + } + CollectionChanged?.Invoke(NotifyCollectionChangedEventArgs.Add(xs.Span, index)); + } + } + } + + public void EnqueueRange(T[] items) + { + lock (SyncRoot) + { + var index = queue.Count; + foreach (var item in items) + { + queue.Enqueue(item); + } + CollectionChanged?.Invoke(NotifyCollectionChangedEventArgs.Add(items, index)); + } + } + + public void EnqueueRange(ReadOnlySpan items) + { + lock (SyncRoot) + { + var index = queue.Count; + foreach (var item in items) + { + queue.Enqueue(item); + } + CollectionChanged?.Invoke(NotifyCollectionChangedEventArgs.Add(items, index)); + } + } + + public T Dequeue() + { + lock (SyncRoot) + { + var v = queue.Dequeue(); + CollectionChanged?.Invoke(NotifyCollectionChangedEventArgs.Remove(v, 0)); + return v; + } + } + + public bool TryDequeue([MaybeNullWhen(false)] out T result) + { + lock (SyncRoot) + { + if (queue.Count != 0) + { + result = queue.Dequeue(); + CollectionChanged?.Invoke(NotifyCollectionChangedEventArgs.Remove(result, 0)); + return true; + } + result = default; + return false; + } + } + + public void DequeueRange(int count) + { + lock (SyncRoot) + { + var dest = ArrayPool.Shared.Rent(count); + try + { + for (int i = 0; i < count; i++) + { + dest[i] = queue.Dequeue(); + } + + CollectionChanged?.Invoke(NotifyCollectionChangedEventArgs.Remove(dest.AsSpan(0, count), 0)); + } + finally + { + ArrayPool.Shared.Return(dest, RuntimeHelpersEx.IsReferenceOrContainsReferences()); + } + } + } + + public void DequeueRange(Span dest) + { + lock (SyncRoot) + { + for (int i = 0; i < dest.Length; i++) + { + dest[i] = queue.Dequeue(); + } + + CollectionChanged?.Invoke(NotifyCollectionChangedEventArgs.Remove(dest, 0)); + } + } + + public void Clear() + { + lock (SyncRoot) + { + queue.Clear(); + CollectionChanged?.Invoke(NotifyCollectionChangedEventArgs.Reset()); + } + } + + public T Peek() + { + lock (SyncRoot) + { + return queue.Peek(); + } + } + + public bool TryPeek([MaybeNullWhen(false)] T result) + { + lock (SyncRoot) + { + if (queue.Count != 0) + { + result = queue.Peek(); + return true; + } + result = default; + return false; + } + } + + public T[] ToArray() + { + lock (SyncRoot) + { + return queue.ToArray(); + } + } + + public void TrimExcess() + { + lock (SyncRoot) + { + queue.TrimExcess(); + } + } + + public IEnumerator GetEnumerator() + { + return new SynchronizedEnumerator(SyncRoot, queue.GetEnumerator()); + } + + IEnumerator IEnumerable.GetEnumerator() + { + return GetEnumerator(); + } + } +} diff --git a/src/ObservableCollections.Unity/Assets/Plugins/ObservableCollections/Runtime/ObservableQueue.cs.meta b/src/ObservableCollections.Unity/Assets/Plugins/ObservableCollections/Runtime/ObservableQueue.cs.meta new file mode 100644 index 0000000..98642b5 --- /dev/null +++ b/src/ObservableCollections.Unity/Assets/Plugins/ObservableCollections/Runtime/ObservableQueue.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: eb4558e36aaf5c84597551bb9a0d42e9 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/src/ObservableCollections.Unity/Assets/Plugins/ObservableCollections/Runtime/ObservableRingBuffer.Views.cs b/src/ObservableCollections.Unity/Assets/Plugins/ObservableCollections/Runtime/ObservableRingBuffer.Views.cs new file mode 100644 index 0000000..539b601 --- /dev/null +++ b/src/ObservableCollections.Unity/Assets/Plugins/ObservableCollections/Runtime/ObservableRingBuffer.Views.cs @@ -0,0 +1,231 @@ +using ObservableCollections.Internal; +using System; +using System.Collections; +using System.Collections.Generic; +using System.Collections.Specialized; +using System.Linq; + +namespace ObservableCollections +{ + public sealed partial class ObservableRingBuffer + { + public ISynchronizedView CreateView(Func transform, bool reverse = false) + { + return new View(this, transform, reverse); + } + + // used with ObservableFixedSizeRingBuffer + internal sealed class View : ISynchronizedView + { + readonly IObservableCollection source; + readonly Func selector; + readonly bool reverse; + readonly RingBuffer<(T, TView)> ringBuffer; + + ISynchronizedViewFilter filter; + + public event NotifyCollectionChangedEventHandler RoutingCollectionChanged; + public event Action CollectionStateChanged; + + public object SyncRoot { get; } + + public View(IObservableCollection source, Func selector, bool reverse) + { + this.source = source; + this.selector = selector; + this.reverse = reverse; + this.filter = SynchronizedViewFilter.Null; + this.SyncRoot = new object(); + lock (source.SyncRoot) + { + this.ringBuffer = new RingBuffer<(T, TView)>(source.Select(x => (x, selector(x)))); + this.source.CollectionChanged += SourceCollectionChanged; + } + } + + public int Count + { + get + { + lock (SyncRoot) + { + return ringBuffer.Count; + } + } + } + + public void AttachFilter(ISynchronizedViewFilter filter) + { + lock (SyncRoot) + { + this.filter = filter; + foreach (var (value, view) in ringBuffer) + { + filter.InvokeOnAttach(value, view); + } + } + } + + public void ResetFilter(Action resetAction) + { + lock (SyncRoot) + { + this.filter = SynchronizedViewFilter.Null; + if (resetAction != null) + { + foreach (var (item, view) in ringBuffer) + { + resetAction(item, view); + } + } + } + } + + public INotifyCollectionChangedSynchronizedView WithINotifyCollectionChanged() + { + lock (SyncRoot) + { + return new NotifyCollectionChangedSynchronizedView(this); + } + } + + public IEnumerator<(T, TView)> GetEnumerator() + { + if (!reverse) + { + return new SynchronizedViewEnumerator(SyncRoot, ringBuffer.AsEnumerable().GetEnumerator(), filter); + } + else + { + return new SynchronizedViewEnumerator(SyncRoot, ringBuffer.AsEnumerable().Reverse().GetEnumerator(), filter); + } + } + + IEnumerator IEnumerable.GetEnumerator() => GetEnumerator(); + + public void Dispose() + { + this.source.CollectionChanged -= SourceCollectionChanged; + } + + private void SourceCollectionChanged(in NotifyCollectionChangedEventArgs e) + { + lock (SyncRoot) + { + switch (e.Action) + { + case NotifyCollectionChangedAction.Add: + // can not distinguish AddFirst and AddLast when collection count is 0. + // So, in that case, use AddLast. + // The internal structure may be different from the parent, but the result is same. + // RangeOperation is only exists AddLastRange because we can not distinguish FirstRange or LastRange. + if (e.NewStartingIndex == 0 && ringBuffer.Count != 0) + { + // AddFirst + if (e.IsSingleItem) + { + var v = (e.NewItem, selector(e.NewItem)); + ringBuffer.AddFirst(v); + filter.InvokeOnAdd(v); + } + else + { + foreach (var item in e.NewItems) + { + var v = (item, selector(item)); + ringBuffer.AddFirst(v); + filter.InvokeOnAdd(v); + } + } + } + else + { + // AddLast + if (e.IsSingleItem) + { + var v = (e.NewItem, selector(e.NewItem)); + ringBuffer.AddLast(v); + filter.InvokeOnAdd(v); + } + else + { + foreach (var item in e.NewItems) + { + var v = (item, selector(item)); + ringBuffer.AddLast(v); + filter.InvokeOnAdd(v); + } + } + } + break; + case NotifyCollectionChangedAction.Remove: + // starting from 0 is RemoveFirst + if (e.OldStartingIndex == 0) + { + // RemoveFirst + if (e.IsSingleItem) + { + var v = ringBuffer.RemoveFirst(); + filter.InvokeOnRemove(v); + } + else + { + for (int i = 0; i < e.OldItems.Length; i++) + { + var v = ringBuffer.RemoveFirst(); + filter.InvokeOnRemove(v); + } + } + } + else + { + // RemoveLast + if (e.IsSingleItem) + { + var v = ringBuffer.RemoveLast(); + filter.InvokeOnRemove(v); + } + else + { + for (int i = 0; i < e.OldItems.Length; i++) + { + var v = ringBuffer.RemoveLast(); + filter.InvokeOnRemove(v); + } + } + } + break; + case NotifyCollectionChangedAction.Reset: + if (!filter.IsNullFilter()) + { + foreach (var item in ringBuffer) + { + filter.InvokeOnRemove(item); + } + } + ringBuffer.Clear(); + break; + case NotifyCollectionChangedAction.Replace: + // range is not supported + { + var v = (e.NewItem, selector(e.NewItem)); + + var oldItem = ringBuffer[e.NewStartingIndex]; + ringBuffer[e.NewStartingIndex] = v; + + filter.InvokeOnRemove(oldItem); + filter.InvokeOnAdd(v); + break; + } + case NotifyCollectionChangedAction.Move: + default: + break; + } + + RoutingCollectionChanged?.Invoke(e); + CollectionStateChanged?.Invoke(e.Action); + } + } + } + } +} \ No newline at end of file diff --git a/src/ObservableCollections.Unity/Assets/Plugins/ObservableCollections/Runtime/ObservableRingBuffer.Views.cs.meta b/src/ObservableCollections.Unity/Assets/Plugins/ObservableCollections/Runtime/ObservableRingBuffer.Views.cs.meta new file mode 100644 index 0000000..c43f22a --- /dev/null +++ b/src/ObservableCollections.Unity/Assets/Plugins/ObservableCollections/Runtime/ObservableRingBuffer.Views.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 9a4b3308e0d0c4c4bafd65b41e7d66cd +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/src/ObservableCollections.Unity/Assets/Plugins/ObservableCollections/Runtime/ObservableRingBuffer.cs b/src/ObservableCollections.Unity/Assets/Plugins/ObservableCollections/Runtime/ObservableRingBuffer.cs new file mode 100644 index 0000000..1301c75 --- /dev/null +++ b/src/ObservableCollections.Unity/Assets/Plugins/ObservableCollections/Runtime/ObservableRingBuffer.cs @@ -0,0 +1,206 @@ +using ObservableCollections.Internal; +using System; +using System.Linq; +using System.Collections.Generic; +using System.Collections; + +namespace ObservableCollections +{ + public sealed partial class ObservableRingBuffer : IList, IReadOnlyList, IObservableCollection + { + readonly RingBuffer buffer; + + public event NotifyCollectionChangedEventHandler CollectionChanged; + + public ObservableRingBuffer() + { + this.buffer = new RingBuffer(); + } + + public ObservableRingBuffer(IEnumerable collection) + { + this.buffer = new RingBuffer(collection); + } + + public bool IsReadOnly => false; + + public object SyncRoot { get; } = new object(); + + public T this[int index] + { + get + { + lock (SyncRoot) + { + return this.buffer[index]; + } + } + set + { + lock (SyncRoot) + { + var oldValue = buffer[index]; + buffer[index] = value; + CollectionChanged?.Invoke(NotifyCollectionChangedEventArgs.Replace(value, oldValue, index)); + } + } + } + + public int Count + { + get + { + lock (SyncRoot) + { + return buffer.Count; + } + } + } + + public void AddFirst(T item) + { + lock (SyncRoot) + { + buffer.AddFirst(item); + CollectionChanged?.Invoke(NotifyCollectionChangedEventArgs.Add(item, 0)); + } + } + + public void AddLast(T item) + { + lock (SyncRoot) + { + buffer.AddLast(item); + CollectionChanged?.Invoke(NotifyCollectionChangedEventArgs.Add(item, buffer.Count - 1)); + } + } + + public T RemoveFirst() + { + lock (SyncRoot) + { + var item = buffer.RemoveFirst(); + CollectionChanged?.Invoke(NotifyCollectionChangedEventArgs.Remove(item, 0)); + return item; + } + } + + public T RemoveLast() + { + lock (SyncRoot) + { + var index = buffer.Count - 1; + var item = buffer.RemoveLast(); + CollectionChanged?.Invoke(NotifyCollectionChangedEventArgs.Remove(item, index)); + return item; + } + } + + // AddFirstRange is not exists. + + public void AddLastRange(IEnumerable items) + { + lock (SyncRoot) + { + var index = buffer.Count; + using (var xs = new CloneCollection(items)) + { + foreach (var item in xs.Span) + { + buffer.AddLast(item); + } + CollectionChanged?.Invoke(NotifyCollectionChangedEventArgs.Add(xs.Span, index)); + } + } + } + + public void AddLastRange(T[] items) + { + lock (SyncRoot) + { + var index = buffer.Count; + foreach (var item in items) + { + buffer.AddLast(item); + } + CollectionChanged?.Invoke(NotifyCollectionChangedEventArgs.Add(items, index)); + } + } + + public void AddLastRange(ReadOnlySpan items) + { + lock (SyncRoot) + { + var index = buffer.Count; + foreach (var item in items) + { + buffer.AddLast(item); + } + CollectionChanged?.Invoke(NotifyCollectionChangedEventArgs.Add(items, index)); + } + } + + public int IndexOf(T item) + { + lock (SyncRoot) + { + return buffer.IndexOf(item); + } + } + + void IList.Insert(int index, T item) + { + throw new NotSupportedException(); + } + + bool ICollection.Remove(T item) + { + throw new NotSupportedException(); + } + + void IList.RemoveAt(int index) + { + throw new NotSupportedException(); + } + + void ICollection.Add(T item) + { + AddLast(item); + } + + public void Clear() + { + lock (SyncRoot) + { + buffer.Clear(); + CollectionChanged?.Invoke(NotifyCollectionChangedEventArgs.Reset()); + } + } + + public bool Contains(T item) + { + lock (SyncRoot) + { + return buffer.Contains(item); + } + } + + public void CopyTo(T[] array, int arrayIndex) + { + lock (SyncRoot) + { + buffer.CopyTo(array, arrayIndex); + } + } + + public IEnumerator GetEnumerator() + { + return new SynchronizedEnumerator(SyncRoot, buffer.GetEnumerator()); + } + + IEnumerator IEnumerable.GetEnumerator() + { + return GetEnumerator(); + } + } +} diff --git a/src/ObservableCollections.Unity/Assets/Plugins/ObservableCollections/Runtime/ObservableRingBuffer.cs.meta b/src/ObservableCollections.Unity/Assets/Plugins/ObservableCollections/Runtime/ObservableRingBuffer.cs.meta new file mode 100644 index 0000000..e46b900 --- /dev/null +++ b/src/ObservableCollections.Unity/Assets/Plugins/ObservableCollections/Runtime/ObservableRingBuffer.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: c858015a9e82dc84487b3e627916a7ff +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/src/ObservableCollections.Unity/Assets/Plugins/ObservableCollections/Runtime/ObservableStack.Views.cs b/src/ObservableCollections.Unity/Assets/Plugins/ObservableCollections/Runtime/ObservableStack.Views.cs new file mode 100644 index 0000000..5e94caf --- /dev/null +++ b/src/ObservableCollections.Unity/Assets/Plugins/ObservableCollections/Runtime/ObservableStack.Views.cs @@ -0,0 +1,173 @@ +using ObservableCollections.Internal; +using System; +using System.Collections; +using System.Collections.Generic; +using System.Collections.Specialized; +using System.Linq; + +namespace ObservableCollections +{ + public sealed partial class ObservableStack : IReadOnlyCollection, IObservableCollection + { + public ISynchronizedView CreateView(Func transform, bool reverse = false) + { + return new View(this, transform, reverse); + } + + class View : ISynchronizedView + { + readonly ObservableStack source; + readonly Func selector; + readonly bool reverse; + protected readonly Stack<(T, TView)> stack; + + ISynchronizedViewFilter filter; + + public event NotifyCollectionChangedEventHandler RoutingCollectionChanged; + public event Action CollectionStateChanged; + + public object SyncRoot { get; } + + public View(ObservableStack source, Func selector, bool reverse) + { + this.source = source; + this.selector = selector; + this.reverse = reverse; + this.filter = SynchronizedViewFilter.Null; + this.SyncRoot = new object(); + lock (source.SyncRoot) + { + this.stack = new Stack<(T, TView)>(source.stack.Select(x => (x, selector(x)))); + this.source.CollectionChanged += SourceCollectionChanged; + } + } + + public int Count + { + get + { + lock (SyncRoot) + { + return stack.Count; + } + } + } + + public void AttachFilter(ISynchronizedViewFilter filter) + { + lock (SyncRoot) + { + this.filter = filter; + foreach (var (value, view) in stack) + { + filter.InvokeOnAttach(value, view); + } + } + } + + public void ResetFilter(Action resetAction) + { + lock (SyncRoot) + { + this.filter = SynchronizedViewFilter.Null; + if (resetAction != null) + { + foreach (var (item, view) in stack) + { + resetAction(item, view); + } + } + } + } + + public INotifyCollectionChangedSynchronizedView WithINotifyCollectionChanged() + { + lock (SyncRoot) + { + return new NotifyCollectionChangedSynchronizedView(this); + } + } + + public IEnumerator<(T, TView)> GetEnumerator() + { + if (!reverse) + { + return new SynchronizedViewEnumerator(SyncRoot, stack.GetEnumerator(), filter); + } + else + { + return new SynchronizedViewEnumerator(SyncRoot, stack.AsEnumerable().Reverse().GetEnumerator(), filter); + } + } + + IEnumerator IEnumerable.GetEnumerator() => GetEnumerator(); + + public void Dispose() + { + this.source.CollectionChanged -= SourceCollectionChanged; + } + + private void SourceCollectionChanged(in NotifyCollectionChangedEventArgs e) + { + lock (SyncRoot) + { + switch (e.Action) + { + case NotifyCollectionChangedAction.Add: + // Add(Push, PushRange) + if (e.IsSingleItem) + { + var v = (e.NewItem, selector(e.NewItem)); + stack.Push(v); + filter.InvokeOnAdd(v); + } + else + { + foreach (var item in e.NewItems) + { + var v = (item, selector(item)); + stack.Push(v); + filter.InvokeOnAdd(v); + } + } + break; + case NotifyCollectionChangedAction.Remove: + // Pop, PopRange + if (e.IsSingleItem) + { + var v = stack.Pop(); + filter.InvokeOnRemove(v.Item1, v.Item2); + } + else + { + var len = e.OldItems.Length; + for (int i = 0; i < len; i++) + { + var v = stack.Pop(); + filter.InvokeOnRemove(v.Item1, v.Item2); + } + } + break; + case NotifyCollectionChangedAction.Reset: + if (!filter.IsNullFilter()) + { + foreach (var item in stack) + { + filter.InvokeOnRemove(item); + } + } + stack.Clear(); + break; + case NotifyCollectionChangedAction.Replace: + case NotifyCollectionChangedAction.Move: + default: + break; + } + + RoutingCollectionChanged?.Invoke(e); + CollectionStateChanged?.Invoke(e.Action); + } + } + } + } +} diff --git a/src/ObservableCollections.Unity/Assets/Plugins/ObservableCollections/Runtime/ObservableStack.Views.cs.meta b/src/ObservableCollections.Unity/Assets/Plugins/ObservableCollections/Runtime/ObservableStack.Views.cs.meta new file mode 100644 index 0000000..dd3c8eb --- /dev/null +++ b/src/ObservableCollections.Unity/Assets/Plugins/ObservableCollections/Runtime/ObservableStack.Views.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: bfc46ddd0f9e1e246931d87579f595b0 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/src/ObservableCollections.Unity/Assets/Plugins/ObservableCollections/Runtime/ObservableStack.cs b/src/ObservableCollections.Unity/Assets/Plugins/ObservableCollections/Runtime/ObservableStack.cs new file mode 100644 index 0000000..6f6748a --- /dev/null +++ b/src/ObservableCollections.Unity/Assets/Plugins/ObservableCollections/Runtime/ObservableStack.cs @@ -0,0 +1,210 @@ +using ObservableCollections.Internal; +using System; +using System.Buffers; +using System.Collections; +using System.Collections.Generic; +using System.Diagnostics.CodeAnalysis; +using System.Runtime.CompilerServices; + +namespace ObservableCollections +{ + public sealed partial class ObservableStack : IReadOnlyCollection, IObservableCollection + { + readonly Stack stack; + public object SyncRoot { get; } = new object(); + + public ObservableStack() + { + this.stack = new Stack(); + } + + public ObservableStack(int capacity) + { + this.stack = new Stack(capacity); + } + + public ObservableStack(IEnumerable collection) + { + this.stack = new Stack(collection); + } + + public event NotifyCollectionChangedEventHandler CollectionChanged; + + public int Count + { + get + { + lock (SyncRoot) + { + return stack.Count; + } + } + } + + public void Push(T item) + { + lock (SyncRoot) + { + var index = stack.Count; + stack.Push(item); + CollectionChanged?.Invoke(NotifyCollectionChangedEventArgs.Add(item, index)); + } + } + + public void PushRange(IEnumerable items) + { + lock (SyncRoot) + { + using (var xs = new CloneCollection(items)) + { + foreach (var item in xs.Span) + { + stack.Push(item); + } + CollectionChanged?.Invoke(NotifyCollectionChangedEventArgs.Add(xs.Span, 0)); + } + } + } + + public void PushRange(T[] items) + { + lock (SyncRoot) + { + foreach (var item in items) + { + stack.Push(item); + } + CollectionChanged?.Invoke(NotifyCollectionChangedEventArgs.Add(items, 0)); + } + } + + public void PushRange(ReadOnlySpan items) + { + lock (SyncRoot) + { + foreach (var item in items) + { + stack.Push(item); + } + CollectionChanged?.Invoke(NotifyCollectionChangedEventArgs.Add(items, 0)); + } + } + + public T Pop() + { + lock (SyncRoot) + { + var v = stack.Pop(); + CollectionChanged?.Invoke(NotifyCollectionChangedEventArgs.Remove(v, 0)); + return v; + } + } + + public bool TryPop([MaybeNullWhen(false)] out T result) + { + lock (SyncRoot) + { + if (stack.Count != 0) + { + result = stack.Pop(); + CollectionChanged?.Invoke(NotifyCollectionChangedEventArgs.Remove(result, 0)); + return true; + } + + result = default; + return false; + } + } + + public void PopRange(int count) + { + lock (SyncRoot) + { + var dest = ArrayPool.Shared.Rent(count); + try + { + for (int i = 0; i < count; i++) + { + dest[i] = stack.Pop(); + } + + CollectionChanged?.Invoke(NotifyCollectionChangedEventArgs.Remove(dest.AsSpan(0, count), 0)); + } + finally + { + ArrayPool.Shared.Return(dest, RuntimeHelpersEx.IsReferenceOrContainsReferences()); + } + } + } + + public void PopRange(Span dest) + { + lock (SyncRoot) + { + for (int i = 0; i < dest.Length; i++) + { + dest[i] = stack.Pop(); + } + + CollectionChanged?.Invoke(NotifyCollectionChangedEventArgs.Remove(dest, 0)); + } + } + + public void Clear() + { + lock (SyncRoot) + { + stack.Clear(); + CollectionChanged?.Invoke(NotifyCollectionChangedEventArgs.Reset()); + } + } + + public T Peek() + { + lock (SyncRoot) + { + return stack.Peek(); + } + } + + public bool TryPeek([MaybeNullWhen(false)] T result) + { + lock (SyncRoot) + { + if (stack.Count != 0) + { + result = stack.Peek(); + return true; + } + result = default; + return false; + } + } + + public T[] ToArray() + { + lock (SyncRoot) + { + return stack.ToArray(); + } + } + + public void TrimExcess() + { + lock (SyncRoot) + { + stack.TrimExcess(); + } + } + + public IEnumerator GetEnumerator() + { + return new SynchronizedEnumerator(SyncRoot, stack.GetEnumerator()); + } + + IEnumerator IEnumerable.GetEnumerator() + { + return GetEnumerator(); + } + } +} \ No newline at end of file diff --git a/src/ObservableCollections.Unity/Assets/Plugins/ObservableCollections/Runtime/ObservableStack.cs.meta b/src/ObservableCollections.Unity/Assets/Plugins/ObservableCollections/Runtime/ObservableStack.cs.meta new file mode 100644 index 0000000..c9e87bc --- /dev/null +++ b/src/ObservableCollections.Unity/Assets/Plugins/ObservableCollections/Runtime/ObservableStack.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: e89128583a1ffee4babe861213e14be2 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/src/ObservableCollections.Unity/Assets/Plugins/ObservableCollections/Runtime/RingBuffer.cs b/src/ObservableCollections.Unity/Assets/Plugins/ObservableCollections/Runtime/RingBuffer.cs new file mode 100644 index 0000000..4adeb8d --- /dev/null +++ b/src/ObservableCollections.Unity/Assets/Plugins/ObservableCollections/Runtime/RingBuffer.cs @@ -0,0 +1,368 @@ +using System; +using System.Collections; +using System.Collections.Generic; +using System.Diagnostics.CodeAnalysis; + +namespace ObservableCollections +{ + public sealed class RingBuffer : IList, IReadOnlyList + { + T[] buffer; + int head; + int count; + int mask; + + public RingBuffer() + { + this.buffer = new T[8]; + this.head = 0; + this.count = 0; + this.mask = buffer.Length - 1; + } + + public RingBuffer(int capacity) + { + this.buffer = new T[CalculateCapacity(capacity)]; + this.head = 0; + this.count = 0; + this.mask = buffer.Length - 1; + } + + public RingBuffer(IEnumerable collection) + { + var array = collection.TryGetNonEnumeratedCount(out var count) + ? new T[CalculateCapacity(count)] + : new T[8]; + var i = 0; + foreach (var item in collection) + { + if (i == array.Length) + { + Array.Resize(ref array, i * 2); + } + array[i++] = item; + } + + this.buffer = array; + this.head = 0; + this.count = i; + this.mask = buffer.Length - 1; + } + + static int CalculateCapacity(int size) + { + size--; + size |= size >> 1; + size |= size >> 2; + size |= size >> 4; + size |= size >> 8; + size |= size >> 16; + size += 1; + + if (size < 8) + { + size = 8; + } + return size; + } + + public T this[int index] + { + get + { + var i = (head + index) & mask; + return buffer[i]; + } + set + { + var i = (head + index) & mask; + buffer[i] = value; + } + } + + public int Count => count; + + public bool IsReadOnly => false; + + public void AddLast(T item) + { + if (count == buffer.Length) EnsureCapacity(); + + var index = (head + count) & mask; + buffer[index] = item; + count++; + } + + public void AddFirst(T item) + { + if (count == buffer.Length) EnsureCapacity(); + + head = (head - 1) & mask; + buffer[head] = item; + count++; + } + + public T RemoveLast() + { + if (count == 0) ThrowForEmpty(); + + var index = (head + count - 1) & mask; + var v = buffer[index]; + buffer[index] = default; + count--; + return v; + } + + public T RemoveFirst() + { + if (count == 0) ThrowForEmpty(); + + var index = head & mask; + var v = buffer[index]; + buffer[index] = default; + head = head + 1; + count--; + return v; + } + + void EnsureCapacity() + { + var newBuffer = new T[buffer.Length * 2]; + + var i = head & mask; + buffer.AsSpan(i).CopyTo(newBuffer); + + if (i != 0) + { + buffer.AsSpan(0, i).CopyTo(newBuffer.AsSpan(buffer.Length - i)); + } + + head = 0; + buffer = newBuffer; + mask = newBuffer.Length - 1; + } + + void ICollection.Add(T item) + { + AddLast(item); + } + + public void Clear() + { + Array.Clear(buffer, 0, buffer.Length); + head = 0; + count = 0; + } + + public RingBufferSpan GetSpan() + { + var start = head & mask; + var end = (head + count) & mask; + + if (end > start) + { + var first = buffer.AsSpan(start, count); + var second = Array.Empty().AsSpan(); + return new RingBufferSpan(first, second, count); + } + else + { + var first = buffer.AsSpan(start, buffer.Length - start); + var second = buffer.AsSpan(0, end); + return new RingBufferSpan(first, second, count); + } + } + + public IEnumerator GetEnumerator() + { + if (count == 0) yield break; + + var start = head & mask; + var end = (head + count) & mask; + + if (end > start) + { + // start...end + for (int i = start; i < end; i++) + { + yield return buffer[i]; + } + } + else + { + // start... + for (int i = start; i < buffer.Length; i++) + { + yield return buffer[i]; + } + // 0...end + for (int i = 0; i < end; i++) + { + yield return buffer[i]; + } + } + } + + public IEnumerable Reverse() + { + var start = head & mask; + var end = (head + count) & mask; + + if (end > start) + { + // end...start + for (int i = end - 1; i >= start; i--) + { + yield return buffer[i]; + } + } + else + { + // end...0 + for (int i = end - 1; i >= 0; i--) + { + yield return buffer[i]; + } + + // ...start + for (int i = buffer.Length - 1; i >= start; i--) + { + yield return buffer[i]; + } + } + } + + public bool Contains(T item) + { + return IndexOf(item) != -1; + } + + public void CopyTo(T[] array, int arrayIndex) + { + var span = GetSpan(); + var dest = array.AsSpan(arrayIndex); + span.First.CopyTo(dest); + span.Second.CopyTo(dest.Slice(span.First.Length)); + } + + public int IndexOf(T item) + { + var i = 0; + foreach (var v in GetSpan()) + { + if (EqualityComparer.Default.Equals(item, v)) + { + return i; + } + i++; + } + return -1; + } + + public T[] ToArray() + { + var result = new T[count]; + var i = 0; + foreach (var item in GetSpan()) + { + result[i++] = item; + } + return result; + } + + void IList.Insert(int index, T item) + { + throw new NotSupportedException(); + } + + bool ICollection.Remove(T item) + { + throw new NotSupportedException(); + } + + void IList.RemoveAt(int index) + { + throw new NotSupportedException(); + } + + IEnumerator IEnumerable.GetEnumerator() + { + return ((IEnumerable)this).GetEnumerator(); + } + + [DoesNotReturn] + static void ThrowForEmpty() + { + throw new InvalidOperationException("RingBuffer is empty."); + } + } + + public ref struct RingBufferSpan + { + public readonly ReadOnlySpan First; + public readonly ReadOnlySpan Second; + public readonly int Count; + + internal RingBufferSpan(ReadOnlySpan first, ReadOnlySpan second, int count) + { + First = first; + Second = second; + Count = count; + } + + public Enumerator GetEnumerator() + { + return new Enumerator(this); + } + + public ref struct Enumerator + { + ReadOnlySpan.Enumerator firstEnumerator; + ReadOnlySpan.Enumerator secondEnumerator; + bool useFirst; + + public Enumerator(RingBufferSpan span) + { + this.firstEnumerator = span.First.GetEnumerator(); + this.secondEnumerator = span.Second.GetEnumerator(); + this.useFirst = true; + } + + public bool MoveNext() + { + if (useFirst) + { + if (firstEnumerator.MoveNext()) + { + return true; + } + else + { + useFirst = false; + } + } + + if (secondEnumerator.MoveNext()) + { + return true; + } + return false; + } + + public T Current + { + get + { + if (useFirst) + { + return firstEnumerator.Current; + } + else + { + return secondEnumerator.Current; + } + } + } + } + } +} \ No newline at end of file diff --git a/src/ObservableCollections.Unity/Assets/Plugins/ObservableCollections/Runtime/RingBuffer.cs.meta b/src/ObservableCollections.Unity/Assets/Plugins/ObservableCollections/Runtime/RingBuffer.cs.meta new file mode 100644 index 0000000..9bfa7e8 --- /dev/null +++ b/src/ObservableCollections.Unity/Assets/Plugins/ObservableCollections/Runtime/RingBuffer.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 090e1b8528f51164695f1ed11c685465 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/src/ObservableCollections.Unity/Assets/Plugins/ObservableCollections/Runtime/Shims.meta b/src/ObservableCollections.Unity/Assets/Plugins/ObservableCollections/Runtime/Shims.meta new file mode 100644 index 0000000..9029b89 --- /dev/null +++ b/src/ObservableCollections.Unity/Assets/Plugins/ObservableCollections/Runtime/Shims.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: 8c8f516891f9df44a847a0bb51a064f0 +folderAsset: yes +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/src/ObservableCollections.Unity/Assets/Plugins/ObservableCollections/Runtime/Shims/Collections.cs b/src/ObservableCollections.Unity/Assets/Plugins/ObservableCollections/Runtime/Shims/Collections.cs new file mode 100644 index 0000000..9be7ce9 --- /dev/null +++ b/src/ObservableCollections.Unity/Assets/Plugins/ObservableCollections/Runtime/Shims/Collections.cs @@ -0,0 +1,64 @@ + + +using System; +using System.Collections.Generic; +using System.Text; + +namespace System.Collections.Generic +{ + internal static class CollectionExtensions + { + public static void Deconstruct(this KeyValuePair kvp, out TKey key, out TValue value) + { + key = kvp.Key; + value = kvp.Value; + } + + public static bool Remove(this SortedDictionary dict, TKey key, out TValue value) + { + if (dict.TryGetValue(key, out value)) + { + return dict.Remove(key); + } + return false; + } + + public static bool Remove(this Dictionary dict, TKey key, out TValue value) + { + if (dict.TryGetValue(key, out value)) + { + return dict.Remove(key); + } + return false; + } + +#if !NET6_0_OR_GREATER + + public static bool TryGetNonEnumeratedCount(this IEnumerable source, out int count) + { + if (source is ICollection collection) + { + count = collection.Count; + return true; + } + if (source is IReadOnlyCollection rCollection) + { + count = rCollection.Count; + return true; + } + count = 0; + return false; + } + +#endif + } + +#if !NET5_0_OR_GREATER + + internal interface IReadOnlySet : System.Collections.Generic.IEnumerable, System.Collections.Generic.IReadOnlyCollection + { + } + +#endif +} + diff --git a/src/ObservableCollections.Unity/Assets/Plugins/ObservableCollections/Runtime/Shims/Collections.cs.meta b/src/ObservableCollections.Unity/Assets/Plugins/ObservableCollections/Runtime/Shims/Collections.cs.meta new file mode 100644 index 0000000..164c51a --- /dev/null +++ b/src/ObservableCollections.Unity/Assets/Plugins/ObservableCollections/Runtime/Shims/Collections.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 02ab78c5678c3bd4ebb45ca99fa0eaea +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/src/ObservableCollections.Unity/Assets/Plugins/ObservableCollections/Runtime/Shims/CompilerServices.cs b/src/ObservableCollections.Unity/Assets/Plugins/ObservableCollections/Runtime/Shims/CompilerServices.cs new file mode 100644 index 0000000..c4fe593 --- /dev/null +++ b/src/ObservableCollections.Unity/Assets/Plugins/ObservableCollections/Runtime/Shims/CompilerServices.cs @@ -0,0 +1,18 @@ +using System; +using System.Collections.Generic; +using System.Text; + +namespace System.Runtime.CompilerServices +{ + internal static class RuntimeHelpersEx + { + internal static bool IsReferenceOrContainsReferences() + { +#if NETSTANDARD2_0 || NET_STANDARD_2_0 || NET_4_6 + return true; +#else + return RuntimeHelpers.IsReferenceOrContainsReferences(); +#endif + } + } +} diff --git a/src/ObservableCollections.Unity/Assets/Plugins/ObservableCollections/Runtime/Shims/CompilerServices.cs.meta b/src/ObservableCollections.Unity/Assets/Plugins/ObservableCollections/Runtime/Shims/CompilerServices.cs.meta new file mode 100644 index 0000000..8a43b0f --- /dev/null +++ b/src/ObservableCollections.Unity/Assets/Plugins/ObservableCollections/Runtime/Shims/CompilerServices.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 90c6c97d9daed56458c4b0f69217c442 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/src/ObservableCollections.Unity/Assets/Plugins/ObservableCollections/Runtime/Shims/Nullables.cs b/src/ObservableCollections.Unity/Assets/Plugins/ObservableCollections/Runtime/Shims/Nullables.cs new file mode 100644 index 0000000..9b745a3 --- /dev/null +++ b/src/ObservableCollections.Unity/Assets/Plugins/ObservableCollections/Runtime/Shims/Nullables.cs @@ -0,0 +1,24 @@ +using System; +using System.Collections.Generic; +using System.Text; + +#if NETSTANDARD2_0 || NET_STANDARD_2_0 || NET_4_6 + +namespace System.Diagnostics.CodeAnalysis +{ + internal sealed class MaybeNullWhenAttribute : Attribute + { + public MaybeNullWhenAttribute(bool returnValue) + { + } + } + + internal sealed class DoesNotReturnAttribute : Attribute + { + public DoesNotReturnAttribute() + { + } + } +} + +#endif \ No newline at end of file diff --git a/src/ObservableCollections.Unity/Assets/Plugins/ObservableCollections/Runtime/Shims/Nullables.cs.meta b/src/ObservableCollections.Unity/Assets/Plugins/ObservableCollections/Runtime/Shims/Nullables.cs.meta new file mode 100644 index 0000000..32b4221 --- /dev/null +++ b/src/ObservableCollections.Unity/Assets/Plugins/ObservableCollections/Runtime/Shims/Nullables.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 2b96ddbca917d2749a613d7dff847f37 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/src/ObservableCollections.Unity/Assets/Plugins/System.Buffers.dll.meta b/src/ObservableCollections.Unity/Assets/Plugins/System.Buffers.dll.meta new file mode 100644 index 0000000..c708ffc --- /dev/null +++ b/src/ObservableCollections.Unity/Assets/Plugins/System.Buffers.dll.meta @@ -0,0 +1,33 @@ +fileFormatVersion: 2 +guid: 80babde185fa5f5f58e1c7c451054bf5 +PluginImporter: + externalObjects: {} + serializedVersion: 2 + iconMap: {} + executionOrder: {} + defineConstraints: [] + isPreloaded: 0 + isOverridable: 0 + isExplicitlyReferenced: 0 + validateReferences: 1 + platformData: + - first: + Any: + second: + enabled: 1 + settings: {} + - first: + Editor: Editor + second: + enabled: 0 + settings: + DefaultValueInitialized: true + - first: + Windows Store Apps: WindowsStoreApps + second: + enabled: 0 + settings: + CPU: AnyCPU + userData: + assetBundleName: + assetBundleVariant: diff --git a/src/ObservableCollections.Unity/Assets/Plugins/System.Memory.dll.meta b/src/ObservableCollections.Unity/Assets/Plugins/System.Memory.dll.meta new file mode 100644 index 0000000..f669027 --- /dev/null +++ b/src/ObservableCollections.Unity/Assets/Plugins/System.Memory.dll.meta @@ -0,0 +1,33 @@ +fileFormatVersion: 2 +guid: 00334fc99a0c324ce9667086377aec59 +PluginImporter: + externalObjects: {} + serializedVersion: 2 + iconMap: {} + executionOrder: {} + defineConstraints: [] + isPreloaded: 0 + isOverridable: 0 + isExplicitlyReferenced: 0 + validateReferences: 1 + platformData: + - first: + Any: + second: + enabled: 1 + settings: {} + - first: + Editor: Editor + second: + enabled: 0 + settings: + DefaultValueInitialized: true + - first: + Windows Store Apps: WindowsStoreApps + second: + enabled: 0 + settings: + CPU: AnyCPU + userData: + assetBundleName: + assetBundleVariant: diff --git a/src/ObservableCollections.Unity/Assets/Plugins/System.Runtime.CompilerServices.Unsafe.dll.meta b/src/ObservableCollections.Unity/Assets/Plugins/System.Runtime.CompilerServices.Unsafe.dll.meta new file mode 100644 index 0000000..f049ae9 --- /dev/null +++ b/src/ObservableCollections.Unity/Assets/Plugins/System.Runtime.CompilerServices.Unsafe.dll.meta @@ -0,0 +1,33 @@ +fileFormatVersion: 2 +guid: 9faef98abc153627f8af30123e43611c +PluginImporter: + externalObjects: {} + serializedVersion: 2 + iconMap: {} + executionOrder: {} + defineConstraints: [] + isPreloaded: 0 + isOverridable: 0 + isExplicitlyReferenced: 0 + validateReferences: 1 + platformData: + - first: + Any: + second: + enabled: 1 + settings: {} + - first: + Editor: Editor + second: + enabled: 0 + settings: + DefaultValueInitialized: true + - first: + Windows Store Apps: WindowsStoreApps + second: + enabled: 0 + settings: + CPU: AnyCPU + userData: + assetBundleName: + assetBundleVariant: diff --git a/src/ObservableCollections.Unity/Assets/Scenes.meta b/src/ObservableCollections.Unity/Assets/Scenes.meta new file mode 100644 index 0000000..c276d6e --- /dev/null +++ b/src/ObservableCollections.Unity/Assets/Scenes.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: 0bc06841af72151429ea8e5bff9677a0 +folderAsset: yes +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/src/ObservableCollections.Unity/Assets/Scenes/SampleScene.unity b/src/ObservableCollections.Unity/Assets/Scenes/SampleScene.unity new file mode 100644 index 0000000..fa3fc63 --- /dev/null +++ b/src/ObservableCollections.Unity/Assets/Scenes/SampleScene.unity @@ -0,0 +1,206 @@ +%YAML 1.1 +%TAG !u! tag:unity3d.com,2011: +--- !u!29 &1 +OcclusionCullingSettings: + m_ObjectHideFlags: 0 + serializedVersion: 2 + m_OcclusionBakeSettings: + smallestOccluder: 5 + smallestHole: 0.25 + backfaceThreshold: 100 + m_SceneGUID: 00000000000000000000000000000000 + m_OcclusionCullingData: {fileID: 0} +--- !u!104 &2 +RenderSettings: + m_ObjectHideFlags: 0 + serializedVersion: 9 + m_Fog: 0 + m_FogColor: {r: 0.5, g: 0.5, b: 0.5, a: 1} + m_FogMode: 3 + m_FogDensity: 0.01 + m_LinearFogStart: 0 + m_LinearFogEnd: 300 + m_AmbientSkyColor: {r: 0.212, g: 0.227, b: 0.259, a: 1} + m_AmbientEquatorColor: {r: 0.114, g: 0.125, b: 0.133, a: 1} + m_AmbientGroundColor: {r: 0.047, g: 0.043, b: 0.035, a: 1} + m_AmbientIntensity: 1 + m_AmbientMode: 3 + m_SubtractiveShadowColor: {r: 0.42, g: 0.478, b: 0.627, a: 1} + m_SkyboxMaterial: {fileID: 0} + m_HaloStrength: 0.5 + m_FlareStrength: 1 + m_FlareFadeSpeed: 3 + m_HaloTexture: {fileID: 0} + m_SpotCookie: {fileID: 10001, guid: 0000000000000000e000000000000000, type: 0} + m_DefaultReflectionMode: 0 + m_DefaultReflectionResolution: 128 + m_ReflectionBounces: 1 + m_ReflectionIntensity: 1 + m_CustomReflection: {fileID: 0} + m_Sun: {fileID: 0} + m_IndirectSpecularColor: {r: 0, g: 0, b: 0, a: 1} + m_UseRadianceAmbientProbe: 0 +--- !u!157 &3 +LightmapSettings: + m_ObjectHideFlags: 0 + serializedVersion: 12 + m_GIWorkflowMode: 1 + m_GISettings: + serializedVersion: 2 + m_BounceScale: 1 + m_IndirectOutputScale: 1 + m_AlbedoBoost: 1 + m_EnvironmentLightingMode: 0 + m_EnableBakedLightmaps: 0 + m_EnableRealtimeLightmaps: 0 + m_LightmapEditorSettings: + serializedVersion: 12 + m_Resolution: 2 + m_BakeResolution: 40 + m_AtlasSize: 1024 + m_AO: 0 + m_AOMaxDistance: 1 + m_CompAOExponent: 1 + m_CompAOExponentDirect: 0 + m_ExtractAmbientOcclusion: 0 + m_Padding: 2 + m_LightmapParameters: {fileID: 0} + m_LightmapsBakeMode: 1 + m_TextureCompression: 1 + m_FinalGather: 0 + m_FinalGatherFiltering: 1 + m_FinalGatherRayCount: 256 + m_ReflectionCompression: 2 + m_MixedBakeMode: 2 + m_BakeBackend: 0 + m_PVRSampling: 1 + m_PVRDirectSampleCount: 32 + m_PVRSampleCount: 500 + m_PVRBounces: 2 + m_PVREnvironmentSampleCount: 500 + m_PVREnvironmentReferencePointCount: 2048 + m_PVRFilteringMode: 2 + m_PVRDenoiserTypeDirect: 0 + m_PVRDenoiserTypeIndirect: 0 + m_PVRDenoiserTypeAO: 0 + m_PVRFilterTypeDirect: 0 + m_PVRFilterTypeIndirect: 0 + m_PVRFilterTypeAO: 0 + m_PVREnvironmentMIS: 0 + m_PVRCulling: 1 + m_PVRFilteringGaussRadiusDirect: 1 + m_PVRFilteringGaussRadiusIndirect: 5 + m_PVRFilteringGaussRadiusAO: 2 + m_PVRFilteringAtrousPositionSigmaDirect: 0.5 + m_PVRFilteringAtrousPositionSigmaIndirect: 2 + m_PVRFilteringAtrousPositionSigmaAO: 1 + m_ExportTrainingData: 0 + m_TrainingDataDestination: TrainingData + m_LightProbeSampleCountMultiplier: 4 + m_LightingDataAsset: {fileID: 0} + m_LightingSettings: {fileID: 0} +--- !u!196 &4 +NavMeshSettings: + serializedVersion: 2 + m_ObjectHideFlags: 0 + m_BuildSettings: + serializedVersion: 2 + agentTypeID: 0 + agentRadius: 0.5 + agentHeight: 2 + agentSlope: 45 + agentClimb: 0.4 + ledgeDropHeight: 0 + maxJumpAcrossDistance: 0 + minRegionArea: 2 + manualCellSize: 0 + cellSize: 0.16666667 + manualTileSize: 0 + tileSize: 256 + accuratePlacement: 0 + debug: + m_Flags: 0 + m_NavMeshData: {fileID: 0} +--- !u!1 &519420028 +GameObject: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + serializedVersion: 6 + m_Component: + - component: {fileID: 519420032} + - component: {fileID: 519420031} + - component: {fileID: 519420029} + m_Layer: 0 + m_Name: Main Camera + m_TagString: MainCamera + m_Icon: {fileID: 0} + m_NavMeshLayer: 0 + m_StaticEditorFlags: 0 + m_IsActive: 1 +--- !u!81 &519420029 +AudioListener: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 519420028} + m_Enabled: 1 +--- !u!20 &519420031 +Camera: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 519420028} + m_Enabled: 1 + serializedVersion: 2 + m_ClearFlags: 2 + m_BackGroundColor: {r: 0.19215687, g: 0.3019608, b: 0.4745098, a: 0} + m_projectionMatrixMode: 1 + m_GateFitMode: 2 + m_FOVAxisMode: 0 + m_SensorSize: {x: 36, y: 24} + m_LensShift: {x: 0, y: 0} + m_FocalLength: 50 + m_NormalizedViewPortRect: + serializedVersion: 2 + x: 0 + y: 0 + width: 1 + height: 1 + near clip plane: 0.3 + far clip plane: 1000 + field of view: 60 + orthographic: 1 + orthographic size: 5 + m_Depth: -1 + m_CullingMask: + serializedVersion: 2 + m_Bits: 4294967295 + m_RenderingPath: -1 + m_TargetTexture: {fileID: 0} + m_TargetDisplay: 0 + m_TargetEye: 0 + m_HDR: 1 + m_AllowMSAA: 0 + m_AllowDynamicResolution: 0 + m_ForceIntoRT: 0 + m_OcclusionCulling: 0 + m_StereoConvergence: 10 + m_StereoSeparation: 0.022 +--- !u!4 &519420032 +Transform: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 519420028} + m_LocalRotation: {x: 0, y: 0, z: 0, w: 1} + m_LocalPosition: {x: 0, y: 0, z: -10} + m_LocalScale: {x: 1, y: 1, z: 1} + m_Children: [] + m_Father: {fileID: 0} + m_RootOrder: 0 + m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} diff --git a/src/ObservableCollections.Unity/Assets/Scenes/SampleScene.unity.meta b/src/ObservableCollections.Unity/Assets/Scenes/SampleScene.unity.meta new file mode 100644 index 0000000..c1e3c88 --- /dev/null +++ b/src/ObservableCollections.Unity/Assets/Scenes/SampleScene.unity.meta @@ -0,0 +1,7 @@ +fileFormatVersion: 2 +guid: 2cda990e2423bbf4892e6590ba056729 +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/src/ObservableCollections.Unity/Packages/manifest.json b/src/ObservableCollections.Unity/Packages/manifest.json new file mode 100644 index 0000000..eb6da04 --- /dev/null +++ b/src/ObservableCollections.Unity/Packages/manifest.json @@ -0,0 +1,43 @@ +{ + "dependencies": { + "com.unity.collab-proxy": "1.2.16", + "com.unity.ext.nunit": "1.0.0", + "com.unity.ide.rider": "1.1.0", + "com.unity.ide.vscode": "1.1.0", + "com.unity.test-framework": "1.1.1", + "com.unity.textmeshpro": "2.0.1", + "com.unity.timeline": "1.2.2", + "com.unity.ugui": "1.0.0", + "com.unity.modules.ai": "1.0.0", + "com.unity.modules.androidjni": "1.0.0", + "com.unity.modules.animation": "1.0.0", + "com.unity.modules.assetbundle": "1.0.0", + "com.unity.modules.audio": "1.0.0", + "com.unity.modules.cloth": "1.0.0", + "com.unity.modules.director": "1.0.0", + "com.unity.modules.imageconversion": "1.0.0", + "com.unity.modules.imgui": "1.0.0", + "com.unity.modules.jsonserialize": "1.0.0", + "com.unity.modules.particlesystem": "1.0.0", + "com.unity.modules.physics": "1.0.0", + "com.unity.modules.physics2d": "1.0.0", + "com.unity.modules.screencapture": "1.0.0", + "com.unity.modules.terrain": "1.0.0", + "com.unity.modules.terrainphysics": "1.0.0", + "com.unity.modules.tilemap": "1.0.0", + "com.unity.modules.ui": "1.0.0", + "com.unity.modules.uielements": "1.0.0", + "com.unity.modules.umbra": "1.0.0", + "com.unity.modules.unityanalytics": "1.0.0", + "com.unity.modules.unitywebrequest": "1.0.0", + "com.unity.modules.unitywebrequestassetbundle": "1.0.0", + "com.unity.modules.unitywebrequestaudio": "1.0.0", + "com.unity.modules.unitywebrequesttexture": "1.0.0", + "com.unity.modules.unitywebrequestwww": "1.0.0", + "com.unity.modules.vehicles": "1.0.0", + "com.unity.modules.video": "1.0.0", + "com.unity.modules.vr": "1.0.0", + "com.unity.modules.wind": "1.0.0", + "com.unity.modules.xr": "1.0.0" + } +} diff --git a/src/ObservableCollections.Unity/ProjectSettings/AudioManager.asset b/src/ObservableCollections.Unity/ProjectSettings/AudioManager.asset new file mode 100644 index 0000000..27287fe --- /dev/null +++ b/src/ObservableCollections.Unity/ProjectSettings/AudioManager.asset @@ -0,0 +1,19 @@ +%YAML 1.1 +%TAG !u! tag:unity3d.com,2011: +--- !u!11 &1 +AudioManager: + m_ObjectHideFlags: 0 + serializedVersion: 2 + m_Volume: 1 + Rolloff Scale: 1 + Doppler Factor: 1 + Default Speaker Mode: 2 + m_SampleRate: 0 + m_DSPBufferSize: 1024 + m_VirtualVoiceCount: 512 + m_RealVoiceCount: 32 + m_SpatializerPlugin: + m_AmbisonicDecoderPlugin: + m_DisableAudio: 0 + m_VirtualizeEffects: 1 + m_RequestedDSPBufferSize: 0 diff --git a/src/ObservableCollections.Unity/ProjectSettings/ClusterInputManager.asset b/src/ObservableCollections.Unity/ProjectSettings/ClusterInputManager.asset new file mode 100644 index 0000000..e7886b2 --- /dev/null +++ b/src/ObservableCollections.Unity/ProjectSettings/ClusterInputManager.asset @@ -0,0 +1,6 @@ +%YAML 1.1 +%TAG !u! tag:unity3d.com,2011: +--- !u!236 &1 +ClusterInputManager: + m_ObjectHideFlags: 0 + m_Inputs: [] diff --git a/src/ObservableCollections.Unity/ProjectSettings/DynamicsManager.asset b/src/ObservableCollections.Unity/ProjectSettings/DynamicsManager.asset new file mode 100644 index 0000000..fc90ab9 --- /dev/null +++ b/src/ObservableCollections.Unity/ProjectSettings/DynamicsManager.asset @@ -0,0 +1,36 @@ +%YAML 1.1 +%TAG !u! tag:unity3d.com,2011: +--- !u!55 &1 +PhysicsManager: + m_ObjectHideFlags: 0 + serializedVersion: 13 + m_Gravity: {x: 0, y: -9.81, z: 0} + m_DefaultMaterial: {fileID: 0} + m_BounceThreshold: 2 + m_SleepThreshold: 0.005 + m_DefaultContactOffset: 0.01 + m_DefaultSolverIterations: 6 + m_DefaultSolverVelocityIterations: 1 + m_QueriesHitBackfaces: 0 + m_QueriesHitTriggers: 1 + m_EnableAdaptiveForce: 0 + m_ClothInterCollisionDistance: 0.1 + m_ClothInterCollisionStiffness: 0.2 + m_ContactsGeneration: 1 + m_LayerCollisionMatrix: ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff + m_AutoSimulation: 1 + m_AutoSyncTransforms: 0 + m_ReuseCollisionCallbacks: 1 + m_ClothInterCollisionSettingsToggle: 0 + m_ClothGravity: {x: 0, y: -9.81, z: 0} + m_ContactPairsMode: 0 + m_BroadphaseType: 0 + m_WorldBounds: + m_Center: {x: 0, y: 0, z: 0} + m_Extent: {x: 250, y: 250, z: 250} + m_WorldSubdivisions: 8 + m_FrictionType: 0 + m_EnableEnhancedDeterminism: 0 + m_EnableUnifiedHeightmaps: 1 + m_SolverType: 0 + m_DefaultMaxAngularSpeed: 50 diff --git a/src/ObservableCollections.Unity/ProjectSettings/EditorBuildSettings.asset b/src/ObservableCollections.Unity/ProjectSettings/EditorBuildSettings.asset new file mode 100644 index 0000000..82ab0f5 --- /dev/null +++ b/src/ObservableCollections.Unity/ProjectSettings/EditorBuildSettings.asset @@ -0,0 +1,11 @@ +%YAML 1.1 +%TAG !u! tag:unity3d.com,2011: +--- !u!1045 &1 +EditorBuildSettings: + m_ObjectHideFlags: 0 + serializedVersion: 2 + m_Scenes: + - enabled: 1 + path: Assets/Scenes/SampleScene.unity + guid: 2cda990e2423bbf4892e6590ba056729 + m_configObjects: {} diff --git a/src/ObservableCollections.Unity/ProjectSettings/EditorSettings.asset b/src/ObservableCollections.Unity/ProjectSettings/EditorSettings.asset new file mode 100644 index 0000000..b1ef674 --- /dev/null +++ b/src/ObservableCollections.Unity/ProjectSettings/EditorSettings.asset @@ -0,0 +1,36 @@ +%YAML 1.1 +%TAG !u! tag:unity3d.com,2011: +--- !u!159 &1 +EditorSettings: + m_ObjectHideFlags: 0 + serializedVersion: 10 + m_ExternalVersionControlSupport: Visible Meta Files + m_SerializationMode: 2 + m_LineEndingsForNewScripts: 0 + m_DefaultBehaviorMode: 1 + m_PrefabRegularEnvironment: {fileID: 0} + m_PrefabUIEnvironment: {fileID: 0} + m_SpritePackerMode: 4 + m_SpritePackerPaddingPower: 1 + m_EtcTextureCompressorBehavior: 1 + m_EtcTextureFastCompressor: 1 + m_EtcTextureNormalCompressor: 2 + m_EtcTextureBestCompressor: 4 + m_ProjectGenerationIncludedExtensions: txt;xml;fnt;cd;asmdef;asmref;rsp + m_ProjectGenerationRootNamespace: + m_CollabEditorSettings: + inProgressEnabled: 1 + m_EnableTextureStreamingInEditMode: 1 + m_EnableTextureStreamingInPlayMode: 1 + m_AsyncShaderCompilation: 1 + m_EnterPlayModeOptionsEnabled: 0 + m_EnterPlayModeOptions: 3 + m_ShowLightmapResolutionOverlay: 1 + m_UseLegacyProbeSampleCount: 0 + m_SerializeInlineMappingsOnOneLine: 1 + m_AssetPipelineMode: 1 + m_CacheServerMode: 0 + m_CacheServerEndpoint: + m_CacheServerNamespacePrefix: default + m_CacheServerEnableDownload: 1 + m_CacheServerEnableUpload: 1 diff --git a/src/ObservableCollections.Unity/ProjectSettings/GraphicsSettings.asset b/src/ObservableCollections.Unity/ProjectSettings/GraphicsSettings.asset new file mode 100644 index 0000000..36bc22d --- /dev/null +++ b/src/ObservableCollections.Unity/ProjectSettings/GraphicsSettings.asset @@ -0,0 +1,63 @@ +%YAML 1.1 +%TAG !u! tag:unity3d.com,2011: +--- !u!30 &1 +GraphicsSettings: + m_ObjectHideFlags: 0 + serializedVersion: 13 + m_Deferred: + m_Mode: 1 + m_Shader: {fileID: 69, guid: 0000000000000000f000000000000000, type: 0} + m_DeferredReflections: + m_Mode: 1 + m_Shader: {fileID: 74, guid: 0000000000000000f000000000000000, type: 0} + m_ScreenSpaceShadows: + m_Mode: 1 + m_Shader: {fileID: 64, guid: 0000000000000000f000000000000000, type: 0} + m_LegacyDeferred: + m_Mode: 1 + m_Shader: {fileID: 63, guid: 0000000000000000f000000000000000, type: 0} + m_DepthNormals: + m_Mode: 1 + m_Shader: {fileID: 62, guid: 0000000000000000f000000000000000, type: 0} + m_MotionVectors: + m_Mode: 1 + m_Shader: {fileID: 75, guid: 0000000000000000f000000000000000, type: 0} + m_LightHalo: + m_Mode: 1 + m_Shader: {fileID: 105, guid: 0000000000000000f000000000000000, type: 0} + m_LensFlare: + m_Mode: 1 + m_Shader: {fileID: 102, guid: 0000000000000000f000000000000000, type: 0} + m_VideoShadersIncludeMode: 2 + m_AlwaysIncludedShaders: + - {fileID: 7, guid: 0000000000000000f000000000000000, type: 0} + - {fileID: 15104, guid: 0000000000000000f000000000000000, type: 0} + - {fileID: 15105, guid: 0000000000000000f000000000000000, type: 0} + - {fileID: 15106, guid: 0000000000000000f000000000000000, type: 0} + - {fileID: 10753, guid: 0000000000000000f000000000000000, type: 0} + - {fileID: 10770, guid: 0000000000000000f000000000000000, type: 0} + - {fileID: 10783, guid: 0000000000000000f000000000000000, type: 0} + m_PreloadedShaders: [] + m_SpritesDefaultMaterial: {fileID: 10754, guid: 0000000000000000f000000000000000, type: 0} + m_CustomRenderPipeline: {fileID: 0} + m_TransparencySortMode: 0 + m_TransparencySortAxis: {x: 0, y: 0, z: 1} + m_DefaultRenderingPath: 1 + m_DefaultMobileRenderingPath: 1 + m_TierSettings: [] + m_LightmapStripping: 0 + m_FogStripping: 0 + m_InstancingStripping: 0 + m_LightmapKeepPlain: 1 + m_LightmapKeepDirCombined: 1 + m_LightmapKeepDynamicPlain: 1 + m_LightmapKeepDynamicDirCombined: 1 + m_LightmapKeepShadowMask: 1 + m_LightmapKeepSubtractive: 1 + m_FogKeepLinear: 1 + m_FogKeepExp: 1 + m_FogKeepExp2: 1 + m_AlbedoSwatchInfos: [] + m_LightsUseLinearIntensity: 0 + m_LightsUseColorTemperature: 0 + m_LogWhenShaderIsCompiled: 0 diff --git a/src/ObservableCollections.Unity/ProjectSettings/InputManager.asset b/src/ObservableCollections.Unity/ProjectSettings/InputManager.asset new file mode 100644 index 0000000..b16147e --- /dev/null +++ b/src/ObservableCollections.Unity/ProjectSettings/InputManager.asset @@ -0,0 +1,487 @@ +%YAML 1.1 +%TAG !u! tag:unity3d.com,2011: +--- !u!13 &1 +InputManager: + m_ObjectHideFlags: 0 + serializedVersion: 2 + m_Axes: + - serializedVersion: 3 + m_Name: Horizontal + descriptiveName: + descriptiveNegativeName: + negativeButton: left + positiveButton: right + altNegativeButton: a + altPositiveButton: d + gravity: 3 + dead: 0.001 + sensitivity: 3 + snap: 1 + invert: 0 + type: 0 + axis: 0 + joyNum: 0 + - serializedVersion: 3 + m_Name: Vertical + descriptiveName: + descriptiveNegativeName: + negativeButton: down + positiveButton: up + altNegativeButton: s + altPositiveButton: w + gravity: 3 + dead: 0.001 + sensitivity: 3 + snap: 1 + invert: 0 + type: 0 + axis: 0 + joyNum: 0 + - serializedVersion: 3 + m_Name: Fire1 + descriptiveName: + descriptiveNegativeName: + negativeButton: + positiveButton: left ctrl + altNegativeButton: + altPositiveButton: mouse 0 + gravity: 1000 + dead: 0.001 + sensitivity: 1000 + snap: 0 + invert: 0 + type: 0 + axis: 0 + joyNum: 0 + - serializedVersion: 3 + m_Name: Fire2 + descriptiveName: + descriptiveNegativeName: + negativeButton: + positiveButton: left alt + altNegativeButton: + altPositiveButton: mouse 1 + gravity: 1000 + dead: 0.001 + sensitivity: 1000 + snap: 0 + invert: 0 + type: 0 + axis: 0 + joyNum: 0 + - serializedVersion: 3 + m_Name: Fire3 + descriptiveName: + descriptiveNegativeName: + negativeButton: + positiveButton: left shift + altNegativeButton: + altPositiveButton: mouse 2 + gravity: 1000 + dead: 0.001 + sensitivity: 1000 + snap: 0 + invert: 0 + type: 0 + axis: 0 + joyNum: 0 + - serializedVersion: 3 + m_Name: Jump + descriptiveName: + descriptiveNegativeName: + negativeButton: + positiveButton: space + altNegativeButton: + altPositiveButton: + gravity: 1000 + dead: 0.001 + sensitivity: 1000 + snap: 0 + invert: 0 + type: 0 + axis: 0 + joyNum: 0 + - serializedVersion: 3 + m_Name: Mouse X + descriptiveName: + descriptiveNegativeName: + negativeButton: + positiveButton: + altNegativeButton: + altPositiveButton: + gravity: 0 + dead: 0 + sensitivity: 0.1 + snap: 0 + invert: 0 + type: 1 + axis: 0 + joyNum: 0 + - serializedVersion: 3 + m_Name: Mouse Y + descriptiveName: + descriptiveNegativeName: + negativeButton: + positiveButton: + altNegativeButton: + altPositiveButton: + gravity: 0 + dead: 0 + sensitivity: 0.1 + snap: 0 + invert: 0 + type: 1 + axis: 1 + joyNum: 0 + - serializedVersion: 3 + m_Name: Mouse ScrollWheel + descriptiveName: + descriptiveNegativeName: + negativeButton: + positiveButton: + altNegativeButton: + altPositiveButton: + gravity: 0 + dead: 0 + sensitivity: 0.1 + snap: 0 + invert: 0 + type: 1 + axis: 2 + joyNum: 0 + - serializedVersion: 3 + m_Name: Horizontal + descriptiveName: + descriptiveNegativeName: + negativeButton: + positiveButton: + altNegativeButton: + altPositiveButton: + gravity: 0 + dead: 0.19 + sensitivity: 1 + snap: 0 + invert: 0 + type: 2 + axis: 0 + joyNum: 0 + - serializedVersion: 3 + m_Name: Vertical + descriptiveName: + descriptiveNegativeName: + negativeButton: + positiveButton: + altNegativeButton: + altPositiveButton: + gravity: 0 + dead: 0.19 + sensitivity: 1 + snap: 0 + invert: 1 + type: 2 + axis: 1 + joyNum: 0 + - serializedVersion: 3 + m_Name: Fire1 + descriptiveName: + descriptiveNegativeName: + negativeButton: + positiveButton: joystick button 0 + altNegativeButton: + altPositiveButton: + gravity: 1000 + dead: 0.001 + sensitivity: 1000 + snap: 0 + invert: 0 + type: 0 + axis: 0 + joyNum: 0 + - serializedVersion: 3 + m_Name: Fire2 + descriptiveName: + descriptiveNegativeName: + negativeButton: + positiveButton: joystick button 1 + altNegativeButton: + altPositiveButton: + gravity: 1000 + dead: 0.001 + sensitivity: 1000 + snap: 0 + invert: 0 + type: 0 + axis: 0 + joyNum: 0 + - serializedVersion: 3 + m_Name: Fire3 + descriptiveName: + descriptiveNegativeName: + negativeButton: + positiveButton: joystick button 2 + altNegativeButton: + altPositiveButton: + gravity: 1000 + dead: 0.001 + sensitivity: 1000 + snap: 0 + invert: 0 + type: 0 + axis: 0 + joyNum: 0 + - serializedVersion: 3 + m_Name: Jump + descriptiveName: + descriptiveNegativeName: + negativeButton: + positiveButton: joystick button 3 + altNegativeButton: + altPositiveButton: + gravity: 1000 + dead: 0.001 + sensitivity: 1000 + snap: 0 + invert: 0 + type: 0 + axis: 0 + joyNum: 0 + - serializedVersion: 3 + m_Name: Submit + descriptiveName: + descriptiveNegativeName: + negativeButton: + positiveButton: return + altNegativeButton: + altPositiveButton: joystick button 0 + gravity: 1000 + dead: 0.001 + sensitivity: 1000 + snap: 0 + invert: 0 + type: 0 + axis: 0 + joyNum: 0 + - serializedVersion: 3 + m_Name: Submit + descriptiveName: + descriptiveNegativeName: + negativeButton: + positiveButton: enter + altNegativeButton: + altPositiveButton: space + gravity: 1000 + dead: 0.001 + sensitivity: 1000 + snap: 0 + invert: 0 + type: 0 + axis: 0 + joyNum: 0 + - serializedVersion: 3 + m_Name: Cancel + descriptiveName: + descriptiveNegativeName: + negativeButton: + positiveButton: escape + altNegativeButton: + altPositiveButton: joystick button 1 + gravity: 1000 + dead: 0.001 + sensitivity: 1000 + snap: 0 + invert: 0 + type: 0 + axis: 0 + joyNum: 0 + - serializedVersion: 3 + m_Name: Enable Debug Button 1 + descriptiveName: + descriptiveNegativeName: + negativeButton: + positiveButton: left ctrl + altNegativeButton: + altPositiveButton: joystick button 8 + gravity: 0 + dead: 0 + sensitivity: 0 + snap: 0 + invert: 0 + type: 0 + axis: 0 + joyNum: 0 + - serializedVersion: 3 + m_Name: Enable Debug Button 2 + descriptiveName: + descriptiveNegativeName: + negativeButton: + positiveButton: backspace + altNegativeButton: + altPositiveButton: joystick button 9 + gravity: 0 + dead: 0 + sensitivity: 0 + snap: 0 + invert: 0 + type: 0 + axis: 0 + joyNum: 0 + - serializedVersion: 3 + m_Name: Debug Reset + descriptiveName: + descriptiveNegativeName: + negativeButton: + positiveButton: left alt + altNegativeButton: + altPositiveButton: joystick button 1 + gravity: 0 + dead: 0 + sensitivity: 0 + snap: 0 + invert: 0 + type: 0 + axis: 0 + joyNum: 0 + - serializedVersion: 3 + m_Name: Debug Next + descriptiveName: + descriptiveNegativeName: + negativeButton: + positiveButton: page down + altNegativeButton: + altPositiveButton: joystick button 5 + gravity: 0 + dead: 0 + sensitivity: 0 + snap: 0 + invert: 0 + type: 0 + axis: 0 + joyNum: 0 + - serializedVersion: 3 + m_Name: Debug Previous + descriptiveName: + descriptiveNegativeName: + negativeButton: + positiveButton: page up + altNegativeButton: + altPositiveButton: joystick button 4 + gravity: 0 + dead: 0 + sensitivity: 0 + snap: 0 + invert: 0 + type: 0 + axis: 0 + joyNum: 0 + - serializedVersion: 3 + m_Name: Debug Validate + descriptiveName: + descriptiveNegativeName: + negativeButton: + positiveButton: return + altNegativeButton: + altPositiveButton: joystick button 0 + gravity: 0 + dead: 0 + sensitivity: 0 + snap: 0 + invert: 0 + type: 0 + axis: 0 + joyNum: 0 + - serializedVersion: 3 + m_Name: Debug Persistent + descriptiveName: + descriptiveNegativeName: + negativeButton: + positiveButton: right shift + altNegativeButton: + altPositiveButton: joystick button 2 + gravity: 0 + dead: 0 + sensitivity: 0 + snap: 0 + invert: 0 + type: 0 + axis: 0 + joyNum: 0 + - serializedVersion: 3 + m_Name: Debug Multiplier + descriptiveName: + descriptiveNegativeName: + negativeButton: + positiveButton: left shift + altNegativeButton: + altPositiveButton: joystick button 3 + gravity: 0 + dead: 0 + sensitivity: 0 + snap: 0 + invert: 0 + type: 0 + axis: 0 + joyNum: 0 + - serializedVersion: 3 + m_Name: Debug Horizontal + descriptiveName: + descriptiveNegativeName: + negativeButton: left + positiveButton: right + altNegativeButton: + altPositiveButton: + gravity: 1000 + dead: 0.001 + sensitivity: 1000 + snap: 0 + invert: 0 + type: 0 + axis: 0 + joyNum: 0 + - serializedVersion: 3 + m_Name: Debug Vertical + descriptiveName: + descriptiveNegativeName: + negativeButton: down + positiveButton: up + altNegativeButton: + altPositiveButton: + gravity: 1000 + dead: 0.001 + sensitivity: 1000 + snap: 0 + invert: 0 + type: 0 + axis: 0 + joyNum: 0 + - serializedVersion: 3 + m_Name: Debug Vertical + descriptiveName: + descriptiveNegativeName: + negativeButton: down + positiveButton: up + altNegativeButton: + altPositiveButton: + gravity: 1000 + dead: 0.001 + sensitivity: 1000 + snap: 0 + invert: 0 + type: 2 + axis: 6 + joyNum: 0 + - serializedVersion: 3 + m_Name: Debug Horizontal + descriptiveName: + descriptiveNegativeName: + negativeButton: left + positiveButton: right + altNegativeButton: + altPositiveButton: + gravity: 1000 + dead: 0.001 + sensitivity: 1000 + snap: 0 + invert: 0 + type: 2 + axis: 5 + joyNum: 0 diff --git a/src/ObservableCollections.Unity/ProjectSettings/NavMeshAreas.asset b/src/ObservableCollections.Unity/ProjectSettings/NavMeshAreas.asset new file mode 100644 index 0000000..3b0b7c3 --- /dev/null +++ b/src/ObservableCollections.Unity/ProjectSettings/NavMeshAreas.asset @@ -0,0 +1,91 @@ +%YAML 1.1 +%TAG !u! tag:unity3d.com,2011: +--- !u!126 &1 +NavMeshProjectSettings: + m_ObjectHideFlags: 0 + serializedVersion: 2 + areas: + - name: Walkable + cost: 1 + - name: Not Walkable + cost: 1 + - name: Jump + cost: 2 + - name: + cost: 1 + - name: + cost: 1 + - name: + cost: 1 + - name: + cost: 1 + - name: + cost: 1 + - name: + cost: 1 + - name: + cost: 1 + - name: + cost: 1 + - name: + cost: 1 + - name: + cost: 1 + - name: + cost: 1 + - name: + cost: 1 + - name: + cost: 1 + - name: + cost: 1 + - name: + cost: 1 + - name: + cost: 1 + - name: + cost: 1 + - name: + cost: 1 + - name: + cost: 1 + - name: + cost: 1 + - name: + cost: 1 + - name: + cost: 1 + - name: + cost: 1 + - name: + cost: 1 + - name: + cost: 1 + - name: + cost: 1 + - name: + cost: 1 + - name: + cost: 1 + - name: + cost: 1 + m_LastAgentTypeID: -887442657 + m_Settings: + - serializedVersion: 2 + agentTypeID: 0 + agentRadius: 0.5 + agentHeight: 2 + agentSlope: 45 + agentClimb: 0.75 + ledgeDropHeight: 0 + maxJumpAcrossDistance: 0 + minRegionArea: 2 + manualCellSize: 0 + cellSize: 0.16666667 + manualTileSize: 0 + tileSize: 256 + accuratePlacement: 0 + debug: + m_Flags: 0 + m_SettingNames: + - Humanoid diff --git a/src/ObservableCollections.Unity/ProjectSettings/NetworkManager.asset b/src/ObservableCollections.Unity/ProjectSettings/NetworkManager.asset new file mode 100644 index 0000000..5dc6a83 --- /dev/null +++ b/src/ObservableCollections.Unity/ProjectSettings/NetworkManager.asset @@ -0,0 +1,8 @@ +%YAML 1.1 +%TAG !u! tag:unity3d.com,2011: +--- !u!149 &1 +NetworkManager: + m_ObjectHideFlags: 0 + m_DebugLevel: 0 + m_Sendrate: 15 + m_AssetToPrefab: {} diff --git a/src/ObservableCollections.Unity/ProjectSettings/PackageManagerSettings.asset b/src/ObservableCollections.Unity/ProjectSettings/PackageManagerSettings.asset new file mode 100644 index 0000000..be4a797 --- /dev/null +++ b/src/ObservableCollections.Unity/ProjectSettings/PackageManagerSettings.asset @@ -0,0 +1,43 @@ +%YAML 1.1 +%TAG !u! tag:unity3d.com,2011: +--- !u!114 &1 +MonoBehaviour: + m_ObjectHideFlags: 61 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 0} + m_Enabled: 1 + m_EditorHideFlags: 0 + m_Script: {fileID: 13964, guid: 0000000000000000e000000000000000, type: 0} + m_Name: + m_EditorClassIdentifier: + m_EnablePreviewPackages: 0 + m_EnablePackageDependencies: 0 + m_AdvancedSettingsExpanded: 1 + m_ScopedRegistriesSettingsExpanded: 1 + oneTimeWarningShown: 0 + m_Registries: + - m_Id: main + m_Name: + m_Url: https://packages.unity.com + m_Scopes: [] + m_IsDefault: 1 + m_Capabilities: 7 + m_UserSelectedRegistryName: + m_UserAddingNewScopedRegistry: 0 + m_RegistryInfoDraft: + m_ErrorMessage: + m_Original: + m_Id: + m_Name: + m_Url: + m_Scopes: [] + m_IsDefault: 0 + m_Capabilities: 0 + m_Modified: 0 + m_Name: + m_Url: + m_Scopes: + - + m_SelectedScopeIndex: 0 diff --git a/src/ObservableCollections.Unity/ProjectSettings/Physics2DSettings.asset b/src/ObservableCollections.Unity/ProjectSettings/Physics2DSettings.asset new file mode 100644 index 0000000..6cfcdda --- /dev/null +++ b/src/ObservableCollections.Unity/ProjectSettings/Physics2DSettings.asset @@ -0,0 +1,56 @@ +%YAML 1.1 +%TAG !u! tag:unity3d.com,2011: +--- !u!19 &1 +Physics2DSettings: + m_ObjectHideFlags: 0 + serializedVersion: 5 + m_Gravity: {x: 0, y: -9.81} + m_DefaultMaterial: {fileID: 0} + m_VelocityIterations: 8 + m_PositionIterations: 3 + m_VelocityThreshold: 1 + m_MaxLinearCorrection: 0.2 + m_MaxAngularCorrection: 8 + m_MaxTranslationSpeed: 100 + m_MaxRotationSpeed: 360 + m_BaumgarteScale: 0.2 + m_BaumgarteTimeOfImpactScale: 0.75 + m_TimeToSleep: 0.5 + m_LinearSleepTolerance: 0.01 + m_AngularSleepTolerance: 2 + m_DefaultContactOffset: 0.01 + m_JobOptions: + serializedVersion: 2 + useMultithreading: 0 + useConsistencySorting: 0 + m_InterpolationPosesPerJob: 100 + m_NewContactsPerJob: 30 + m_CollideContactsPerJob: 100 + m_ClearFlagsPerJob: 200 + m_ClearBodyForcesPerJob: 200 + m_SyncDiscreteFixturesPerJob: 50 + m_SyncContinuousFixturesPerJob: 50 + m_FindNearestContactsPerJob: 100 + m_UpdateTriggerContactsPerJob: 100 + m_IslandSolverCostThreshold: 100 + m_IslandSolverBodyCostScale: 1 + m_IslandSolverContactCostScale: 10 + m_IslandSolverJointCostScale: 10 + m_IslandSolverBodiesPerJob: 50 + m_IslandSolverContactsPerJob: 50 + m_SimulationMode: 0 + m_QueriesHitTriggers: 1 + m_QueriesStartInColliders: 1 + m_CallbacksOnDisable: 1 + m_ReuseCollisionCallbacks: 1 + m_AutoSyncTransforms: 0 + m_AlwaysShowColliders: 0 + m_ShowColliderSleep: 1 + m_ShowColliderContacts: 0 + m_ShowColliderAABB: 0 + m_ContactArrowScale: 0.2 + m_ColliderAwakeColor: {r: 0.5686275, g: 0.95686275, b: 0.54509807, a: 0.7529412} + m_ColliderAsleepColor: {r: 0.5686275, g: 0.95686275, b: 0.54509807, a: 0.36078432} + m_ColliderContactColor: {r: 1, g: 0, b: 1, a: 0.6862745} + m_ColliderAABBColor: {r: 1, g: 1, b: 0, a: 0.2509804} + m_LayerCollisionMatrix: ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff diff --git a/src/ObservableCollections.Unity/ProjectSettings/PresetManager.asset b/src/ObservableCollections.Unity/ProjectSettings/PresetManager.asset new file mode 100644 index 0000000..67a94da --- /dev/null +++ b/src/ObservableCollections.Unity/ProjectSettings/PresetManager.asset @@ -0,0 +1,7 @@ +%YAML 1.1 +%TAG !u! tag:unity3d.com,2011: +--- !u!1386491679 &1 +PresetManager: + m_ObjectHideFlags: 0 + serializedVersion: 2 + m_DefaultPresets: {} diff --git a/src/ObservableCollections.Unity/ProjectSettings/ProjectSettings.asset b/src/ObservableCollections.Unity/ProjectSettings/ProjectSettings.asset new file mode 100644 index 0000000..4eda366 --- /dev/null +++ b/src/ObservableCollections.Unity/ProjectSettings/ProjectSettings.asset @@ -0,0 +1,646 @@ +%YAML 1.1 +%TAG !u! tag:unity3d.com,2011: +--- !u!129 &1 +PlayerSettings: + m_ObjectHideFlags: 0 + serializedVersion: 22 + productGUID: 4852b04bea0e36b428b29c34acc83ad9 + AndroidProfiler: 0 + AndroidFilterTouchesWhenObscured: 0 + AndroidEnableSustainedPerformanceMode: 0 + defaultScreenOrientation: 4 + targetDevice: 2 + useOnDemandResources: 0 + accelerometerFrequency: 60 + companyName: DefaultCompany + productName: ObservableCollections.Unity + defaultCursor: {fileID: 0} + cursorHotspot: {x: 0, y: 0} + m_SplashScreenBackgroundColor: {r: 0.13725491, g: 0.12156863, b: 0.1254902, a: 1} + m_ShowUnitySplashScreen: 1 + m_ShowUnitySplashLogo: 1 + m_SplashScreenOverlayOpacity: 1 + m_SplashScreenAnimation: 1 + m_SplashScreenLogoStyle: 1 + m_SplashScreenDrawMode: 0 + m_SplashScreenBackgroundAnimationZoom: 1 + m_SplashScreenLogoAnimationZoom: 1 + m_SplashScreenBackgroundLandscapeAspect: 1 + m_SplashScreenBackgroundPortraitAspect: 1 + m_SplashScreenBackgroundLandscapeUvs: + serializedVersion: 2 + x: 0 + y: 0 + width: 1 + height: 1 + m_SplashScreenBackgroundPortraitUvs: + serializedVersion: 2 + x: 0 + y: 0 + width: 1 + height: 1 + m_SplashScreenLogos: [] + m_VirtualRealitySplashScreen: {fileID: 0} + m_HolographicTrackingLossScreen: {fileID: 0} + defaultScreenWidth: 1920 + defaultScreenHeight: 1080 + defaultScreenWidthWeb: 960 + defaultScreenHeightWeb: 600 + m_StereoRenderingPath: 0 + m_ActiveColorSpace: 0 + m_MTRendering: 1 + mipStripping: 0 + numberOfMipsStripped: 0 + m_StackTraceTypes: 010000000100000001000000010000000100000001000000 + iosShowActivityIndicatorOnLoading: -1 + androidShowActivityIndicatorOnLoading: -1 + iosUseCustomAppBackgroundBehavior: 0 + iosAllowHTTPDownload: 1 + allowedAutorotateToPortrait: 1 + allowedAutorotateToPortraitUpsideDown: 1 + allowedAutorotateToLandscapeRight: 1 + allowedAutorotateToLandscapeLeft: 1 + useOSAutorotation: 1 + use32BitDisplayBuffer: 1 + preserveFramebufferAlpha: 0 + disableDepthAndStencilBuffers: 0 + androidStartInFullscreen: 1 + androidRenderOutsideSafeArea: 1 + androidUseSwappy: 1 + androidBlitType: 0 + defaultIsNativeResolution: 1 + macRetinaSupport: 1 + runInBackground: 0 + captureSingleScreen: 0 + muteOtherAudioSources: 0 + Prepare IOS For Recording: 0 + Force IOS Speakers When Recording: 0 + deferSystemGesturesMode: 0 + hideHomeButton: 0 + submitAnalytics: 1 + usePlayerLog: 1 + bakeCollisionMeshes: 0 + forceSingleInstance: 0 + useFlipModelSwapchain: 1 + resizableWindow: 0 + useMacAppStoreValidation: 0 + macAppStoreCategory: public.app-category.games + gpuSkinning: 0 + xboxPIXTextureCapture: 0 + xboxEnableAvatar: 0 + xboxEnableKinect: 0 + xboxEnableKinectAutoTracking: 0 + xboxEnableFitness: 0 + visibleInBackground: 1 + allowFullscreenSwitch: 1 + fullscreenMode: 1 + xboxSpeechDB: 0 + xboxEnableHeadOrientation: 0 + xboxEnableGuest: 0 + xboxEnablePIXSampling: 0 + metalFramebufferOnly: 0 + xboxOneResolution: 0 + xboxOneSResolution: 0 + xboxOneXResolution: 3 + xboxOneMonoLoggingLevel: 0 + xboxOneLoggingLevel: 1 + xboxOneDisableEsram: 0 + xboxOneEnableTypeOptimization: 0 + xboxOnePresentImmediateThreshold: 0 + switchQueueCommandMemory: 1048576 + switchQueueControlMemory: 16384 + switchQueueComputeMemory: 262144 + switchNVNShaderPoolsGranularity: 33554432 + switchNVNDefaultPoolsGranularity: 16777216 + switchNVNOtherPoolsGranularity: 16777216 + switchNVNMaxPublicTextureIDCount: 0 + switchNVNMaxPublicSamplerIDCount: 0 + stadiaPresentMode: 0 + stadiaTargetFramerate: 0 + vulkanNumSwapchainBuffers: 3 + vulkanEnableSetSRGBWrite: 0 + vulkanEnablePreTransform: 0 + vulkanEnableLateAcquireNextImage: 0 + m_SupportedAspectRatios: + 4:3: 1 + 5:4: 1 + 16:10: 1 + 16:9: 1 + Others: 1 + bundleVersion: 1.0 + preloadedAssets: [] + metroInputSource: 0 + wsaTransparentSwapchain: 0 + m_HolographicPauseOnTrackingLoss: 1 + xboxOneDisableKinectGpuReservation: 1 + xboxOneEnable7thCore: 1 + vrSettings: + enable360StereoCapture: 0 + isWsaHolographicRemotingEnabled: 0 + enableFrameTimingStats: 0 + useHDRDisplay: 0 + D3DHDRBitDepth: 0 + m_ColorGamuts: 00000000 + targetPixelDensity: 30 + resolutionScalingMode: 0 + androidSupportedAspectRatio: 1 + androidMaxAspectRatio: 2.1 + applicationIdentifier: {} + buildNumber: + Standalone: 0 + iPhone: 0 + tvOS: 0 + overrideDefaultApplicationIdentifier: 0 + AndroidBundleVersionCode: 1 + AndroidMinSdkVersion: 19 + AndroidTargetSdkVersion: 0 + AndroidPreferredInstallLocation: 1 + aotOptions: + stripEngineCode: 1 + iPhoneStrippingLevel: 0 + iPhoneScriptCallOptimization: 0 + ForceInternetPermission: 0 + ForceSDCardPermission: 0 + CreateWallpaper: 0 + APKExpansionFiles: 0 + keepLoadedShadersAlive: 0 + StripUnusedMeshComponents: 0 + VertexChannelCompressionMask: 4054 + iPhoneSdkVersion: 988 + iOSTargetOSVersionString: 11.0 + tvOSSdkVersion: 0 + tvOSRequireExtendedGameController: 0 + tvOSTargetOSVersionString: 11.0 + uIPrerenderedIcon: 0 + uIRequiresPersistentWiFi: 0 + uIRequiresFullScreen: 1 + uIStatusBarHidden: 1 + uIExitOnSuspend: 0 + uIStatusBarStyle: 0 + appleTVSplashScreen: {fileID: 0} + appleTVSplashScreen2x: {fileID: 0} + tvOSSmallIconLayers: [] + tvOSSmallIconLayers2x: [] + tvOSLargeIconLayers: [] + tvOSLargeIconLayers2x: [] + tvOSTopShelfImageLayers: [] + tvOSTopShelfImageLayers2x: [] + tvOSTopShelfImageWideLayers: [] + tvOSTopShelfImageWideLayers2x: [] + iOSLaunchScreenType: 0 + iOSLaunchScreenPortrait: {fileID: 0} + iOSLaunchScreenLandscape: {fileID: 0} + iOSLaunchScreenBackgroundColor: + serializedVersion: 2 + rgba: 0 + iOSLaunchScreenFillPct: 100 + iOSLaunchScreenSize: 100 + iOSLaunchScreenCustomXibPath: + iOSLaunchScreeniPadType: 0 + iOSLaunchScreeniPadImage: {fileID: 0} + iOSLaunchScreeniPadBackgroundColor: + serializedVersion: 2 + rgba: 0 + iOSLaunchScreeniPadFillPct: 100 + iOSLaunchScreeniPadSize: 100 + iOSLaunchScreeniPadCustomXibPath: + iOSLaunchScreenCustomStoryboardPath: + iOSLaunchScreeniPadCustomStoryboardPath: + iOSDeviceRequirements: [] + iOSURLSchemes: [] + iOSBackgroundModes: 0 + iOSMetalForceHardShadows: 0 + metalEditorSupport: 1 + metalAPIValidation: 1 + iOSRenderExtraFrameOnPause: 0 + iosCopyPluginsCodeInsteadOfSymlink: 0 + appleDeveloperTeamID: + iOSManualSigningProvisioningProfileID: + tvOSManualSigningProvisioningProfileID: + iOSManualSigningProvisioningProfileType: 0 + tvOSManualSigningProvisioningProfileType: 0 + appleEnableAutomaticSigning: 0 + iOSRequireARKit: 0 + iOSAutomaticallyDetectAndAddCapabilities: 1 + appleEnableProMotion: 0 + shaderPrecisionModel: 0 + clonedFromGUID: 10ad67313f4034357812315f3c407484 + templatePackageId: com.unity.template.2d@5.0.0 + templateDefaultScene: Assets/Scenes/SampleScene.unity + useCustomMainManifest: 0 + useCustomLauncherManifest: 0 + useCustomMainGradleTemplate: 0 + useCustomLauncherGradleManifest: 0 + useCustomBaseGradleTemplate: 0 + useCustomGradlePropertiesTemplate: 0 + useCustomProguardFile: 0 + AndroidTargetArchitectures: 1 + AndroidSplashScreenScale: 0 + androidSplashScreen: {fileID: 0} + AndroidKeystoreName: + AndroidKeyaliasName: + AndroidBuildApkPerCpuArchitecture: 0 + AndroidTVCompatibility: 0 + AndroidIsGame: 1 + AndroidEnableTango: 0 + androidEnableBanner: 1 + androidUseLowAccuracyLocation: 0 + androidUseCustomKeystore: 0 + m_AndroidBanners: + - width: 320 + height: 180 + banner: {fileID: 0} + androidGamepadSupportLevel: 0 + AndroidMinifyWithR8: 0 + AndroidMinifyRelease: 0 + AndroidMinifyDebug: 0 + AndroidValidateAppBundleSize: 1 + AndroidAppBundleSizeToValidate: 150 + m_BuildTargetIcons: [] + m_BuildTargetPlatformIcons: [] + m_BuildTargetBatching: [] + m_BuildTargetGraphicsJobs: + - m_BuildTarget: MacStandaloneSupport + m_GraphicsJobs: 0 + - m_BuildTarget: Switch + m_GraphicsJobs: 0 + - m_BuildTarget: MetroSupport + m_GraphicsJobs: 0 + - m_BuildTarget: AppleTVSupport + m_GraphicsJobs: 0 + - m_BuildTarget: BJMSupport + m_GraphicsJobs: 0 + - m_BuildTarget: LinuxStandaloneSupport + m_GraphicsJobs: 0 + - m_BuildTarget: PS4Player + m_GraphicsJobs: 0 + - m_BuildTarget: iOSSupport + m_GraphicsJobs: 0 + - m_BuildTarget: WindowsStandaloneSupport + m_GraphicsJobs: 0 + - m_BuildTarget: XboxOnePlayer + m_GraphicsJobs: 0 + - m_BuildTarget: LuminSupport + m_GraphicsJobs: 0 + - m_BuildTarget: AndroidPlayer + m_GraphicsJobs: 0 + - m_BuildTarget: WebGLSupport + m_GraphicsJobs: 0 + m_BuildTargetGraphicsJobMode: [] + m_BuildTargetGraphicsAPIs: + - m_BuildTarget: AndroidPlayer + m_APIs: 150000000b000000 + m_Automatic: 0 + - m_BuildTarget: iOSSupport + m_APIs: 10000000 + m_Automatic: 1 + m_BuildTargetVRSettings: [] + openGLRequireES31: 0 + openGLRequireES31AEP: 0 + openGLRequireES32: 0 + m_TemplateCustomTags: {} + mobileMTRendering: + Android: 1 + iPhone: 1 + tvOS: 1 + m_BuildTargetGroupLightmapEncodingQuality: [] + m_BuildTargetGroupLightmapSettings: [] + m_BuildTargetNormalMapEncoding: [] + playModeTestRunnerEnabled: 0 + runPlayModeTestAsEditModeTest: 0 + actionOnDotNetUnhandledException: 1 + enableInternalProfiler: 0 + logObjCUncaughtExceptions: 1 + enableCrashReportAPI: 0 + cameraUsageDescription: + locationUsageDescription: + microphoneUsageDescription: + switchNMETAOverride: + switchNetLibKey: + switchSocketMemoryPoolSize: 6144 + switchSocketAllocatorPoolSize: 128 + switchSocketConcurrencyLimit: 14 + switchScreenResolutionBehavior: 2 + switchUseCPUProfiler: 0 + switchUseGOLDLinker: 0 + switchApplicationID: 0x01004b9000490000 + switchNSODependencies: + switchTitleNames_0: + switchTitleNames_1: + switchTitleNames_2: + switchTitleNames_3: + switchTitleNames_4: + switchTitleNames_5: + switchTitleNames_6: + switchTitleNames_7: + switchTitleNames_8: + switchTitleNames_9: + switchTitleNames_10: + switchTitleNames_11: + switchTitleNames_12: + switchTitleNames_13: + switchTitleNames_14: + switchTitleNames_15: + switchPublisherNames_0: + switchPublisherNames_1: + switchPublisherNames_2: + switchPublisherNames_3: + switchPublisherNames_4: + switchPublisherNames_5: + switchPublisherNames_6: + switchPublisherNames_7: + switchPublisherNames_8: + switchPublisherNames_9: + switchPublisherNames_10: + switchPublisherNames_11: + switchPublisherNames_12: + switchPublisherNames_13: + switchPublisherNames_14: + switchPublisherNames_15: + switchIcons_0: {fileID: 0} + switchIcons_1: {fileID: 0} + switchIcons_2: {fileID: 0} + switchIcons_3: {fileID: 0} + switchIcons_4: {fileID: 0} + switchIcons_5: {fileID: 0} + switchIcons_6: {fileID: 0} + switchIcons_7: {fileID: 0} + switchIcons_8: {fileID: 0} + switchIcons_9: {fileID: 0} + switchIcons_10: {fileID: 0} + switchIcons_11: {fileID: 0} + switchIcons_12: {fileID: 0} + switchIcons_13: {fileID: 0} + switchIcons_14: {fileID: 0} + switchIcons_15: {fileID: 0} + switchSmallIcons_0: {fileID: 0} + switchSmallIcons_1: {fileID: 0} + switchSmallIcons_2: {fileID: 0} + switchSmallIcons_3: {fileID: 0} + switchSmallIcons_4: {fileID: 0} + switchSmallIcons_5: {fileID: 0} + switchSmallIcons_6: {fileID: 0} + switchSmallIcons_7: {fileID: 0} + switchSmallIcons_8: {fileID: 0} + switchSmallIcons_9: {fileID: 0} + switchSmallIcons_10: {fileID: 0} + switchSmallIcons_11: {fileID: 0} + switchSmallIcons_12: {fileID: 0} + switchSmallIcons_13: {fileID: 0} + switchSmallIcons_14: {fileID: 0} + switchSmallIcons_15: {fileID: 0} + switchManualHTML: + switchAccessibleURLs: + switchLegalInformation: + switchMainThreadStackSize: 1048576 + switchPresenceGroupId: + switchLogoHandling: 0 + switchReleaseVersion: 0 + switchDisplayVersion: 1.0.0 + switchStartupUserAccount: 0 + switchTouchScreenUsage: 0 + switchSupportedLanguagesMask: 0 + switchLogoType: 0 + switchApplicationErrorCodeCategory: + switchUserAccountSaveDataSize: 0 + switchUserAccountSaveDataJournalSize: 0 + switchApplicationAttribute: 0 + switchCardSpecSize: -1 + switchCardSpecClock: -1 + switchRatingsMask: 0 + switchRatingsInt_0: 0 + switchRatingsInt_1: 0 + switchRatingsInt_2: 0 + switchRatingsInt_3: 0 + switchRatingsInt_4: 0 + switchRatingsInt_5: 0 + switchRatingsInt_6: 0 + switchRatingsInt_7: 0 + switchRatingsInt_8: 0 + switchRatingsInt_9: 0 + switchRatingsInt_10: 0 + switchRatingsInt_11: 0 + switchRatingsInt_12: 0 + switchLocalCommunicationIds_0: + switchLocalCommunicationIds_1: + switchLocalCommunicationIds_2: + switchLocalCommunicationIds_3: + switchLocalCommunicationIds_4: + switchLocalCommunicationIds_5: + switchLocalCommunicationIds_6: + switchLocalCommunicationIds_7: + switchParentalControl: 0 + switchAllowsScreenshot: 1 + switchAllowsVideoCapturing: 1 + switchAllowsRuntimeAddOnContentInstall: 0 + switchDataLossConfirmation: 0 + switchUserAccountLockEnabled: 0 + switchSystemResourceMemory: 16777216 + switchSupportedNpadStyles: 22 + switchNativeFsCacheSize: 32 + switchIsHoldTypeHorizontal: 0 + switchSupportedNpadCount: 8 + switchSocketConfigEnabled: 0 + switchTcpInitialSendBufferSize: 32 + switchTcpInitialReceiveBufferSize: 64 + switchTcpAutoSendBufferSizeMax: 256 + switchTcpAutoReceiveBufferSizeMax: 256 + switchUdpSendBufferSize: 9 + switchUdpReceiveBufferSize: 42 + switchSocketBufferEfficiency: 4 + switchSocketInitializeEnabled: 1 + switchNetworkInterfaceManagerInitializeEnabled: 1 + switchPlayerConnectionEnabled: 1 + switchUseNewStyleFilepaths: 0 + switchUseMicroSleepForYield: 1 + switchMicroSleepForYieldTime: 25 + ps4NPAgeRating: 12 + ps4NPTitleSecret: + ps4NPTrophyPackPath: + ps4ParentalLevel: 11 + ps4ContentID: ED1633-NPXX51362_00-0000000000000000 + ps4Category: 0 + ps4MasterVersion: 01.00 + ps4AppVersion: 01.00 + ps4AppType: 0 + ps4ParamSfxPath: + ps4VideoOutPixelFormat: 0 + ps4VideoOutInitialWidth: 1920 + ps4VideoOutBaseModeInitialWidth: 1920 + ps4VideoOutReprojectionRate: 60 + ps4PronunciationXMLPath: + ps4PronunciationSIGPath: + ps4BackgroundImagePath: + ps4StartupImagePath: + ps4StartupImagesFolder: + ps4IconImagesFolder: + ps4SaveDataImagePath: + ps4SdkOverride: + ps4BGMPath: + ps4ShareFilePath: + ps4ShareOverlayImagePath: + ps4PrivacyGuardImagePath: + ps4ExtraSceSysFile: + ps4NPtitleDatPath: + ps4RemotePlayKeyAssignment: -1 + ps4RemotePlayKeyMappingDir: + ps4PlayTogetherPlayerCount: 0 + ps4EnterButtonAssignment: 2 + ps4ApplicationParam1: 0 + ps4ApplicationParam2: 0 + ps4ApplicationParam3: 0 + ps4ApplicationParam4: 0 + ps4DownloadDataSize: 0 + ps4GarlicHeapSize: 2048 + ps4ProGarlicHeapSize: 2560 + playerPrefsMaxSize: 32768 + ps4Passcode: bi9UOuSpM2Tlh01vOzwvSikHFswuzleh + ps4pnSessions: 1 + ps4pnPresence: 1 + ps4pnFriends: 1 + ps4pnGameCustomData: 1 + playerPrefsSupport: 0 + enableApplicationExit: 0 + resetTempFolder: 1 + restrictedAudioUsageRights: 0 + ps4UseResolutionFallback: 0 + ps4ReprojectionSupport: 0 + ps4UseAudio3dBackend: 0 + ps4UseLowGarlicFragmentationMode: 1 + ps4SocialScreenEnabled: 0 + ps4ScriptOptimizationLevel: 2 + ps4Audio3dVirtualSpeakerCount: 14 + ps4attribCpuUsage: 0 + ps4PatchPkgPath: + ps4PatchLatestPkgPath: + ps4PatchChangeinfoPath: + ps4PatchDayOne: 0 + ps4attribUserManagement: 0 + ps4attribMoveSupport: 0 + ps4attrib3DSupport: 0 + ps4attribShareSupport: 0 + ps4attribExclusiveVR: 0 + ps4disableAutoHideSplash: 0 + ps4videoRecordingFeaturesUsed: 0 + ps4contentSearchFeaturesUsed: 0 + ps4CompatibilityPS5: 0 + ps4GPU800MHz: 1 + ps4attribEyeToEyeDistanceSettingVR: 0 + ps4IncludedModules: [] + ps4attribVROutputEnabled: 0 + monoEnv: + splashScreenBackgroundSourceLandscape: {fileID: 0} + splashScreenBackgroundSourcePortrait: {fileID: 0} + blurSplashScreenBackground: 1 + spritePackerPolicy: + webGLMemorySize: 32 + webGLExceptionSupport: 1 + webGLNameFilesAsHashes: 0 + webGLDataCaching: 1 + webGLDebugSymbols: 0 + webGLEmscriptenArgs: + webGLModulesDirectory: + webGLTemplate: APPLICATION:Default + webGLAnalyzeBuildSize: 0 + webGLUseEmbeddedResources: 0 + webGLCompressionFormat: 0 + webGLWasmArithmeticExceptions: 0 + webGLLinkerTarget: 1 + webGLThreadsSupport: 0 + webGLDecompressionFallback: 0 + scriptingDefineSymbols: {} + additionalCompilerArguments: {} + platformArchitecture: {} + scriptingBackend: {} + il2cppCompilerConfiguration: {} + managedStrippingLevel: {} + incrementalIl2cppBuild: {} + suppressCommonWarnings: 1 + allowUnsafeCode: 0 + useDeterministicCompilation: 1 + useReferenceAssemblies: 1 + enableRoslynAnalyzers: 1 + additionalIl2CppArgs: + scriptingRuntimeVersion: 1 + gcIncremental: 1 + assemblyVersionValidation: 1 + gcWBarrierValidation: 0 + apiCompatibilityLevelPerPlatform: {} + m_RenderingPath: 1 + m_MobileRenderingPath: 1 + metroPackageName: 2D_BuiltInRenderer + metroPackageVersion: + metroCertificatePath: + metroCertificatePassword: + metroCertificateSubject: + metroCertificateIssuer: + metroCertificateNotAfter: 0000000000000000 + metroApplicationDescription: 2D_BuiltInRenderer + wsaImages: {} + metroTileShortName: + metroTileShowName: 0 + metroMediumTileShowName: 0 + metroLargeTileShowName: 0 + metroWideTileShowName: 0 + metroSupportStreamingInstall: 0 + metroLastRequiredScene: 0 + metroDefaultTileSize: 1 + metroTileForegroundText: 2 + metroTileBackgroundColor: {r: 0.13333334, g: 0.17254902, b: 0.21568628, a: 0} + metroSplashScreenBackgroundColor: {r: 0.12941177, g: 0.17254902, b: 0.21568628, a: 1} + metroSplashScreenUseBackgroundColor: 0 + platformCapabilities: {} + metroTargetDeviceFamilies: {} + metroFTAName: + metroFTAFileTypes: [] + metroProtocolName: + XboxOneProductId: + XboxOneUpdateKey: + XboxOneSandboxId: + XboxOneContentId: + XboxOneTitleId: + XboxOneSCId: + XboxOneGameOsOverridePath: + XboxOnePackagingOverridePath: + XboxOneAppManifestOverridePath: + XboxOneVersion: 1.0.0.0 + XboxOnePackageEncryption: 0 + XboxOnePackageUpdateGranularity: 2 + XboxOneDescription: + XboxOneLanguage: + - enus + XboxOneCapability: [] + XboxOneGameRating: {} + XboxOneIsContentPackage: 0 + XboxOneEnhancedXboxCompatibilityMode: 0 + XboxOneEnableGPUVariability: 1 + XboxOneSockets: {} + XboxOneSplashScreen: {fileID: 0} + XboxOneAllowedProductIds: [] + XboxOnePersistentLocalStorageSize: 0 + XboxOneXTitleMemory: 8 + XboxOneOverrideIdentityName: + XboxOneOverrideIdentityPublisher: + vrEditorSettings: {} + cloudServicesEnabled: {} + luminIcon: + m_Name: + m_ModelFolderPath: + m_PortalFolderPath: + luminCert: + m_CertPath: + m_SignPackage: 1 + luminIsChannelApp: 0 + luminVersion: + m_VersionCode: 1 + m_VersionName: + apiCompatibilityLevel: 6 + activeInputHandler: 0 + cloudProjectId: + framebufferDepthMemorylessMode: 0 + qualitySettingsNames: [] + projectName: + organizationId: + cloudEnabled: 0 + legacyClampBlendShapeWeights: 0 + virtualTexturingSupportEnabled: 0 diff --git a/src/ObservableCollections.Unity/ProjectSettings/ProjectVersion.txt b/src/ObservableCollections.Unity/ProjectSettings/ProjectVersion.txt new file mode 100644 index 0000000..4452b0b --- /dev/null +++ b/src/ObservableCollections.Unity/ProjectSettings/ProjectVersion.txt @@ -0,0 +1,2 @@ +m_EditorVersion: 2020.1.0a3 +m_EditorVersionWithRevision: 2020.1.0a3 (a37e4d4d532f) diff --git a/src/ObservableCollections.Unity/ProjectSettings/QualitySettings.asset b/src/ObservableCollections.Unity/ProjectSettings/QualitySettings.asset new file mode 100644 index 0000000..d24eb10 --- /dev/null +++ b/src/ObservableCollections.Unity/ProjectSettings/QualitySettings.asset @@ -0,0 +1,236 @@ +%YAML 1.1 +%TAG !u! tag:unity3d.com,2011: +--- !u!47 &1 +QualitySettings: + m_ObjectHideFlags: 0 + serializedVersion: 5 + m_CurrentQuality: 5 + m_QualitySettings: + - serializedVersion: 2 + name: Very Low + pixelLightCount: 0 + shadows: 0 + shadowResolution: 0 + shadowProjection: 1 + shadowCascades: 1 + shadowDistance: 15 + shadowNearPlaneOffset: 3 + shadowCascade2Split: 0.33333334 + shadowCascade4Split: {x: 0.06666667, y: 0.2, z: 0.46666667} + shadowmaskMode: 0 + skinWeights: 1 + textureQuality: 1 + anisotropicTextures: 0 + antiAliasing: 0 + softParticles: 0 + softVegetation: 0 + realtimeReflectionProbes: 0 + billboardsFaceCameraPosition: 0 + vSyncCount: 0 + lodBias: 0.3 + maximumLODLevel: 0 + streamingMipmapsActive: 0 + streamingMipmapsAddAllCameras: 1 + streamingMipmapsMemoryBudget: 512 + streamingMipmapsRenderersPerFrame: 512 + streamingMipmapsMaxLevelReduction: 2 + streamingMipmapsMaxFileIORequests: 1024 + particleRaycastBudget: 4 + asyncUploadTimeSlice: 2 + asyncUploadBufferSize: 16 + asyncUploadPersistentBuffer: 1 + resolutionScalingFixedDPIFactor: 1 + customRenderPipeline: {fileID: 0} + excludedTargetPlatforms: [] + - serializedVersion: 2 + name: Low + pixelLightCount: 0 + shadows: 0 + shadowResolution: 0 + shadowProjection: 1 + shadowCascades: 1 + shadowDistance: 20 + shadowNearPlaneOffset: 3 + shadowCascade2Split: 0.33333334 + shadowCascade4Split: {x: 0.06666667, y: 0.2, z: 0.46666667} + shadowmaskMode: 0 + skinWeights: 2 + textureQuality: 0 + anisotropicTextures: 0 + antiAliasing: 0 + softParticles: 0 + softVegetation: 0 + realtimeReflectionProbes: 0 + billboardsFaceCameraPosition: 0 + vSyncCount: 0 + lodBias: 0.4 + maximumLODLevel: 0 + streamingMipmapsActive: 0 + streamingMipmapsAddAllCameras: 1 + streamingMipmapsMemoryBudget: 512 + streamingMipmapsRenderersPerFrame: 512 + streamingMipmapsMaxLevelReduction: 2 + streamingMipmapsMaxFileIORequests: 1024 + particleRaycastBudget: 16 + asyncUploadTimeSlice: 2 + asyncUploadBufferSize: 16 + asyncUploadPersistentBuffer: 1 + resolutionScalingFixedDPIFactor: 1 + customRenderPipeline: {fileID: 0} + excludedTargetPlatforms: [] + - serializedVersion: 2 + name: Medium + pixelLightCount: 1 + shadows: 1 + shadowResolution: 0 + shadowProjection: 1 + shadowCascades: 1 + shadowDistance: 20 + shadowNearPlaneOffset: 3 + shadowCascade2Split: 0.33333334 + shadowCascade4Split: {x: 0.06666667, y: 0.2, z: 0.46666667} + shadowmaskMode: 0 + skinWeights: 2 + textureQuality: 0 + anisotropicTextures: 1 + antiAliasing: 0 + softParticles: 0 + softVegetation: 0 + realtimeReflectionProbes: 0 + billboardsFaceCameraPosition: 0 + vSyncCount: 1 + lodBias: 0.7 + maximumLODLevel: 0 + streamingMipmapsActive: 0 + streamingMipmapsAddAllCameras: 1 + streamingMipmapsMemoryBudget: 512 + streamingMipmapsRenderersPerFrame: 512 + streamingMipmapsMaxLevelReduction: 2 + streamingMipmapsMaxFileIORequests: 1024 + particleRaycastBudget: 64 + asyncUploadTimeSlice: 2 + asyncUploadBufferSize: 16 + asyncUploadPersistentBuffer: 1 + resolutionScalingFixedDPIFactor: 1 + customRenderPipeline: {fileID: 0} + excludedTargetPlatforms: [] + - serializedVersion: 2 + name: High + pixelLightCount: 2 + shadows: 2 + shadowResolution: 1 + shadowProjection: 1 + shadowCascades: 2 + shadowDistance: 40 + shadowNearPlaneOffset: 3 + shadowCascade2Split: 0.33333334 + shadowCascade4Split: {x: 0.06666667, y: 0.2, z: 0.46666667} + shadowmaskMode: 1 + skinWeights: 2 + textureQuality: 0 + anisotropicTextures: 1 + antiAliasing: 0 + softParticles: 0 + softVegetation: 1 + realtimeReflectionProbes: 1 + billboardsFaceCameraPosition: 1 + vSyncCount: 1 + lodBias: 1 + maximumLODLevel: 0 + streamingMipmapsActive: 0 + streamingMipmapsAddAllCameras: 1 + streamingMipmapsMemoryBudget: 512 + streamingMipmapsRenderersPerFrame: 512 + streamingMipmapsMaxLevelReduction: 2 + streamingMipmapsMaxFileIORequests: 1024 + particleRaycastBudget: 256 + asyncUploadTimeSlice: 2 + asyncUploadBufferSize: 16 + asyncUploadPersistentBuffer: 1 + resolutionScalingFixedDPIFactor: 1 + customRenderPipeline: {fileID: 0} + excludedTargetPlatforms: [] + - serializedVersion: 2 + name: Very High + pixelLightCount: 3 + shadows: 2 + shadowResolution: 2 + shadowProjection: 1 + shadowCascades: 2 + shadowDistance: 70 + shadowNearPlaneOffset: 3 + shadowCascade2Split: 0.33333334 + shadowCascade4Split: {x: 0.06666667, y: 0.2, z: 0.46666667} + shadowmaskMode: 1 + skinWeights: 4 + textureQuality: 0 + anisotropicTextures: 2 + antiAliasing: 2 + softParticles: 1 + softVegetation: 1 + realtimeReflectionProbes: 1 + billboardsFaceCameraPosition: 1 + vSyncCount: 1 + lodBias: 1.5 + maximumLODLevel: 0 + streamingMipmapsActive: 0 + streamingMipmapsAddAllCameras: 1 + streamingMipmapsMemoryBudget: 512 + streamingMipmapsRenderersPerFrame: 512 + streamingMipmapsMaxLevelReduction: 2 + streamingMipmapsMaxFileIORequests: 1024 + particleRaycastBudget: 1024 + asyncUploadTimeSlice: 2 + asyncUploadBufferSize: 16 + asyncUploadPersistentBuffer: 1 + resolutionScalingFixedDPIFactor: 1 + customRenderPipeline: {fileID: 0} + excludedTargetPlatforms: [] + - serializedVersion: 2 + name: Ultra + pixelLightCount: 4 + shadows: 2 + shadowResolution: 2 + shadowProjection: 1 + shadowCascades: 4 + shadowDistance: 150 + shadowNearPlaneOffset: 3 + shadowCascade2Split: 0.33333334 + shadowCascade4Split: {x: 0.06666667, y: 0.2, z: 0.46666667} + shadowmaskMode: 1 + skinWeights: 255 + textureQuality: 0 + anisotropicTextures: 2 + antiAliasing: 2 + softParticles: 1 + softVegetation: 1 + realtimeReflectionProbes: 1 + billboardsFaceCameraPosition: 1 + vSyncCount: 1 + lodBias: 2 + maximumLODLevel: 0 + streamingMipmapsActive: 0 + streamingMipmapsAddAllCameras: 1 + streamingMipmapsMemoryBudget: 512 + streamingMipmapsRenderersPerFrame: 512 + streamingMipmapsMaxLevelReduction: 2 + streamingMipmapsMaxFileIORequests: 1024 + particleRaycastBudget: 4096 + asyncUploadTimeSlice: 2 + asyncUploadBufferSize: 16 + asyncUploadPersistentBuffer: 1 + resolutionScalingFixedDPIFactor: 1 + customRenderPipeline: {fileID: 0} + excludedTargetPlatforms: [] + m_PerPlatformDefaultQuality: + Android: 2 + Lumin: 5 + Nintendo Switch: 5 + PS4: 5 + Stadia: 5 + Standalone: 5 + WebGL: 3 + Windows Store Apps: 5 + XboxOne: 5 + iPhone: 2 + tvOS: 2 diff --git a/src/ObservableCollections.Unity/ProjectSettings/TagManager.asset b/src/ObservableCollections.Unity/ProjectSettings/TagManager.asset new file mode 100644 index 0000000..1c92a78 --- /dev/null +++ b/src/ObservableCollections.Unity/ProjectSettings/TagManager.asset @@ -0,0 +1,43 @@ +%YAML 1.1 +%TAG !u! tag:unity3d.com,2011: +--- !u!78 &1 +TagManager: + serializedVersion: 2 + tags: [] + layers: + - Default + - TransparentFX + - Ignore Raycast + - + - Water + - UI + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + m_SortingLayers: + - name: Default + uniqueID: 0 + locked: 0 diff --git a/src/ObservableCollections.Unity/ProjectSettings/TimeManager.asset b/src/ObservableCollections.Unity/ProjectSettings/TimeManager.asset new file mode 100644 index 0000000..558a017 --- /dev/null +++ b/src/ObservableCollections.Unity/ProjectSettings/TimeManager.asset @@ -0,0 +1,9 @@ +%YAML 1.1 +%TAG !u! tag:unity3d.com,2011: +--- !u!5 &1 +TimeManager: + m_ObjectHideFlags: 0 + Fixed Timestep: 0.02 + Maximum Allowed Timestep: 0.33333334 + m_TimeScale: 1 + Maximum Particle Timestep: 0.03 diff --git a/src/ObservableCollections.Unity/ProjectSettings/UnityConnectSettings.asset b/src/ObservableCollections.Unity/ProjectSettings/UnityConnectSettings.asset new file mode 100644 index 0000000..6125b30 --- /dev/null +++ b/src/ObservableCollections.Unity/ProjectSettings/UnityConnectSettings.asset @@ -0,0 +1,35 @@ +%YAML 1.1 +%TAG !u! tag:unity3d.com,2011: +--- !u!310 &1 +UnityConnectSettings: + m_ObjectHideFlags: 0 + serializedVersion: 1 + m_Enabled: 0 + m_TestMode: 0 + m_EventOldUrl: https://api.uca.cloud.unity3d.com/v1/events + m_EventUrl: https://cdp.cloud.unity3d.com/v1/events + m_ConfigUrl: https://config.uca.cloud.unity3d.com + m_DashboardUrl: https://dashboard.unity3d.com + m_TestInitMode: 0 + CrashReportingSettings: + m_EventUrl: https://perf-events.cloud.unity3d.com + m_Enabled: 0 + m_LogBufferSize: 10 + m_CaptureEditorExceptions: 1 + UnityPurchasingSettings: + m_Enabled: 0 + m_TestMode: 0 + UnityAnalyticsSettings: + m_Enabled: 0 + m_TestMode: 0 + m_InitializeOnStartup: 1 + UnityAdsSettings: + m_Enabled: 0 + m_InitializeOnStartup: 1 + m_TestMode: 0 + m_IosGameId: + m_AndroidGameId: + m_GameIds: {} + m_GameId: + PerformanceReportingSettings: + m_Enabled: 0 diff --git a/src/ObservableCollections.Unity/ProjectSettings/VFXManager.asset b/src/ObservableCollections.Unity/ProjectSettings/VFXManager.asset new file mode 100644 index 0000000..3a95c98 --- /dev/null +++ b/src/ObservableCollections.Unity/ProjectSettings/VFXManager.asset @@ -0,0 +1,12 @@ +%YAML 1.1 +%TAG !u! tag:unity3d.com,2011: +--- !u!937362698 &1 +VFXManager: + m_ObjectHideFlags: 0 + m_IndirectShader: {fileID: 0} + m_CopyBufferShader: {fileID: 0} + m_SortShader: {fileID: 0} + m_StripUpdateShader: {fileID: 0} + m_RenderPipeSettingsPath: + m_FixedTimeStep: 0.016666668 + m_MaxDeltaTime: 0.05 diff --git a/src/ObservableCollections.Unity/ProjectSettings/VersionControlSettings.asset b/src/ObservableCollections.Unity/ProjectSettings/VersionControlSettings.asset new file mode 100644 index 0000000..dca2881 --- /dev/null +++ b/src/ObservableCollections.Unity/ProjectSettings/VersionControlSettings.asset @@ -0,0 +1,8 @@ +%YAML 1.1 +%TAG !u! tag:unity3d.com,2011: +--- !u!890905787 &1 +VersionControlSettings: + m_ObjectHideFlags: 0 + m_Mode: Visible Meta Files + m_CollabEditorSettings: + inProgressEnabled: 1 diff --git a/src/ObservableCollections.Unity/ProjectSettings/XRSettings.asset b/src/ObservableCollections.Unity/ProjectSettings/XRSettings.asset new file mode 100644 index 0000000..482590c --- /dev/null +++ b/src/ObservableCollections.Unity/ProjectSettings/XRSettings.asset @@ -0,0 +1,10 @@ +{ + "m_SettingKeys": [ + "VR Device Disabled", + "VR Device User Alert" + ], + "m_SettingValues": [ + "False", + "False" + ] +} \ No newline at end of file diff --git a/src/ObservableCollections.Unity/UserSettings/EditorUserSettings.asset b/src/ObservableCollections.Unity/UserSettings/EditorUserSettings.asset new file mode 100644 index 0000000..b543455 --- /dev/null +++ b/src/ObservableCollections.Unity/UserSettings/EditorUserSettings.asset @@ -0,0 +1,24 @@ +%YAML 1.1 +%TAG !u! tag:unity3d.com,2011: +--- !u!162 &1 +EditorUserSettings: + m_ObjectHideFlags: 0 + serializedVersion: 4 + m_ConfigSettings: + RecentlyUsedScenePath-0: + value: 22424703114646680e0b0227036c6c111b07142f1f2b233e2867083debf42d + flags: 0 + vcSharedLogLevel: + value: 0d5e400f0650 + flags: 0 + m_VCAutomaticAdd: 1 + m_VCDebugCom: 0 + m_VCDebugCmd: 0 + m_VCDebugOut: 0 + m_SemanticMergeMode: 2 + m_VCShowFailedCheckout: 1 + m_VCOverwriteFailedCheckoutAssets: 1 + m_VCProjectOverlayIcons: 1 + m_VCHierarchyOverlayIcons: 1 + m_VCOtherOverlayIcons: 1 + m_VCAllowAsyncUpdate: 1 diff --git a/tools/PostBuildUtility/PostBuildUtility.csproj b/tools/PostBuildUtility/PostBuildUtility.csproj new file mode 100644 index 0000000..94830bc --- /dev/null +++ b/tools/PostBuildUtility/PostBuildUtility.csproj @@ -0,0 +1,12 @@ + + + + Exe + net6.0 + enable + + + + + + diff --git a/tools/PostBuildUtility/Program.cs b/tools/PostBuildUtility/Program.cs new file mode 100644 index 0000000..0f9afd1 --- /dev/null +++ b/tools/PostBuildUtility/Program.cs @@ -0,0 +1,62 @@ +using ConsoleAppFramework; +using Microsoft.Extensions.Hosting; +using System; +using System.Collections.Generic; +using System.IO; +using System.Text; +using System.Threading; +using System.Threading.Tasks; + +namespace PostBuildUtility +{ + class Program : ConsoleAppBase + { + static async Task Main(string[] args) + { + await Host.CreateDefaultBuilder().RunConsoleAppFrameworkAsync(args); + } + + [Command("replace-to-unity")] + public void ReplaceToUnity([Option(0)] string directory) + { + var replaceSet = new Dictionary + { + // Remove nullable + {"#nullable disable", "" }, + {"where T : notnull", "" }, + {"where TKey : notnull", "" }, + {">?", ">" }, // generics ? + {"T?", "T" }, + {"T[]?", "T[]" }, + {"default!", "default" }, + {"null!", "null" }, + // project specified + {"array!", "array" }, + {"Current!", "Current" }, + {"NotifyCollectionChangedEventHandler?", "NotifyCollectionChangedEventHandler" }, + {"PropertyChangedEventHandler?", "PropertyChangedEventHandler" }, + }; + + System.Console.WriteLine("Start to replace code, remove nullability."); + var noBomUtf8 = new UTF8Encoding(false); + + foreach (var path in Directory.EnumerateFiles(directory, "*.cs", SearchOption.AllDirectories)) + { + var text = File.ReadAllText(path, Encoding.UTF8); + var original = text; + + foreach (var item in replaceSet) + { + text = text.Replace(item.Key, item.Value); + } + + if (text != original) + { + File.WriteAllText(path, text, noBomUtf8); + } + } + + System.Console.WriteLine("Replace complete."); + } + } +} \ No newline at end of file