using System; using System.Collections.Specialized; namespace ObservableCollections { public readonly struct SynchronizedViewChangedEventArgs( NotifyCollectionChangedAction action, T newValue = default!, T oldValue = default!, TView newView = default!, TView oldView = default!, int newViewIndex = -1, int oldViewIndex = -1) { public readonly NotifyCollectionChangedAction Action = action; public readonly T NewValue = newValue; public readonly T OldValue = oldValue; public readonly TView NewView = newView; public readonly TView OldView = oldView; public readonly int NewViewIndex = newViewIndex; public readonly int OldViewIndex = oldViewIndex; } public interface ISynchronizedViewFilter { bool IsMatch(T value); } public class SynchronizedViewFilter(Func isMatch) : ISynchronizedViewFilter { public static readonly ISynchronizedViewFilter Null = new NullViewFilter(); public bool IsMatch(T value) => isMatch(value); class NullViewFilter : ISynchronizedViewFilter { public bool IsMatch(T value) => true; } } public static class SynchronizedViewFilterExtensions { public static void AttachFilter(this ISynchronizedView source, Func filter) { source.AttachFilter(new SynchronizedViewFilter(filter)); } public static bool IsNullFilter(this ISynchronizedViewFilter filter) { return filter == SynchronizedViewFilter.Null; } internal static void InvokeOnAdd(this ISynchronizedView collection, ref int filteredCount, Action>? ev, (T value, TView view) value, int index) { InvokeOnAdd(collection, ref filteredCount, ev, value.value, value.view, index); } internal static void InvokeOnAdd(this ISynchronizedView collection, ref int filteredCount, Action>? ev, T value, TView view, int index) { var isMatch = collection.CurrentFilter.IsMatch(value); if (isMatch) { filteredCount++; if (ev != null) { ev.Invoke(new SynchronizedViewChangedEventArgs(NotifyCollectionChangedAction.Add, newValue: value, newView: view, newViewIndex: index)); } } } internal static void InvokeOnRemove(this ISynchronizedView collection, ref int filteredCount, Action>? ev, (T value, TView view) value, int oldIndex) { InvokeOnRemove(collection, ref filteredCount, ev, value.value, value.view, oldIndex); } internal static void InvokeOnRemove(this ISynchronizedView collection, ref int filteredCount, Action>? ev, T value, TView view, int oldIndex) { var isMatch = collection.CurrentFilter.IsMatch(value); if (isMatch) { filteredCount--; if (ev != null) { ev.Invoke(new SynchronizedViewChangedEventArgs(NotifyCollectionChangedAction.Remove, isMatch, oldValue: value, oldView: view, oldViewIndex: oldIndex)); } } } internal static void InvokeOnMove(this ISynchronizedView collection, ref int filteredCount, Action>? ev, (T value, TView view) value, int index, int oldIndex) { InvokeOnMove(collection, ref filteredCount, ev, value.value, value.view, index, oldIndex); } internal static void InvokeOnMove(this ISynchronizedView collection, ref int filteredCount, Action>? ev, T value, TView view, int index, int oldIndex) { if (ev != null) { // move does not changes filtered-count var isMatch = collection.CurrentFilter.IsMatch(value); if (isMatch) { ev.Invoke(new SynchronizedViewChangedEventArgs(NotifyCollectionChangedAction.Move, newValue: value, newView: view, newViewIndex: index, oldViewIndex: oldIndex)); } } } internal static void InvokeOnReplace(this ISynchronizedView collection, ref int filteredCount, Action>? ev, (T value, TView view) value, (T value, TView view) oldValue, int index, int oldIndex = -1) { InvokeOnReplace(collection, ref filteredCount, ev, value.value, value.view, oldValue.value, oldValue.view, index, oldIndex); } internal static void InvokeOnReplace(this ISynchronizedView collection, ref int filteredCount, Action>? ev, T value, TView view, T oldValue, TView oldView, int index, int oldIndex = -1) { var oldMatched = collection.CurrentFilter.IsMatch(oldValue); var newMatched = collection.CurrentFilter.IsMatch(value); var bothMatched = oldMatched && newMatched; // TODO:...! if (bothMatched) { } else if (oldMatched) { // only-old is remove } else if (newMatched) { // only-new is add } if (ev != null) { var isMatch = collection.CurrentFilter.IsMatch(value); ev.Invoke(new SynchronizedViewChangedEventArgs(NotifyCollectionChangedAction.Replace, isMatch, newValue: value, newView: view, oldValue: oldValue, oldView: oldView, newViewIndex: index, oldViewIndex: oldIndex >= 0 ? oldIndex : index)); } } internal static void InvokeOnReset(this ISynchronizedView collection, ref int filteredCount, Action>? ev) { filteredCount = 0; if (ev != null) { ev.Invoke(new SynchronizedViewChangedEventArgs(NotifyCollectionChangedAction.Reset, true)); } } 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); } } } }