This commit is contained in:
neuecc 2024-08-21 17:00:28 +09:00
parent ffa8a97e35
commit 4aacf11bee
4 changed files with 106 additions and 94 deletions

View File

@ -21,7 +21,7 @@ namespace ObservableCollections
{ {
} }
public interface IReadOnlyObservableDictionary<TKey, TValue> : public interface IReadOnlyObservableDictionary<TKey, TValue> :
IReadOnlyDictionary<TKey, TValue>, IObservableCollection<KeyValuePair<TKey, TValue>> IReadOnlyDictionary<TKey, TValue>, IObservableCollection<KeyValuePair<TKey, TValue>>
{ {
} }
@ -35,16 +35,16 @@ namespace ObservableCollections
public interface ISynchronizedView<T, TView> : IReadOnlyCollection<(T Value, TView View)>, IDisposable public interface ISynchronizedView<T, TView> : IReadOnlyCollection<(T Value, TView View)>, IDisposable
{ {
object SyncRoot { get; } object SyncRoot { get; }
ISynchronizedViewFilter<T, TView> CurrentFilter { get; } ISynchronizedViewFilter<T> CurrentFilter { get; }
// TODO: add // TODO: add
event Action<SynchronizedViewChangedEventArgs<T,TView>>? ViewChanged; event Action<SynchronizedViewChangedEventArgs<T, TView>>? ViewChanged;
// TODO: remove // TODO: remove
event NotifyCollectionChangedEventHandler<T>? RoutingCollectionChanged; // event NotifyCollectionChangedEventHandler<T>? RoutingCollectionChanged;
event Action<NotifyCollectionChangedAction>? CollectionStateChanged; event Action<NotifyCollectionChangedAction>? CollectionStateChanged;
void AttachFilter(ISynchronizedViewFilter<T, TView> filter, bool invokeAddEventForInitialElements = false); void AttachFilter(ISynchronizedViewFilter<T> filter, bool invokeAddEventForInitialElements = false);
void ResetFilter(Action<T, TView>? resetAction); void ResetFilter(Action<T>? resetAction);
INotifyCollectionChangedSynchronizedView<TView> ToNotifyCollectionChanged(); INotifyCollectionChangedSynchronizedView<TView> ToNotifyCollectionChanged();
INotifyCollectionChangedSynchronizedView<TView> ToNotifyCollectionChanged(ICollectionEventDispatcher? collectionEventDispatcher); INotifyCollectionChangedSynchronizedView<TView> ToNotifyCollectionChanged(ICollectionEventDispatcher? collectionEventDispatcher);
} }

View File

@ -21,111 +21,130 @@ namespace ObservableCollections
public readonly int OldViewIndex = oldViewIndex; public readonly int OldViewIndex = oldViewIndex;
} }
public interface ISynchronizedViewFilter<T, TView> public interface ISynchronizedViewFilter<T>
{ {
bool IsMatch(T value, TView view); bool IsMatch(T value);
void WhenTrue(T value, TView view);
void WhenFalse(T value, TView view);
void OnCollectionChanged(in SynchronizedViewChangedEventArgs<T, TView> eventArgs);
} }
public class SynchronizedViewFilter<T, TView>( public class SynchronizedViewFilter<T>(Func<T, bool> isMatch) : ISynchronizedViewFilter<T>
Func<T, TView, bool> isMatch,
Action<T, TView>? whenTrue,
Action<T, TView>? whenFalse,
Action<SynchronizedViewChangedEventArgs<T, TView>>? onCollectionChanged)
: ISynchronizedViewFilter<T, TView>
{ {
public static readonly ISynchronizedViewFilter<T, TView> Null = new NullViewFilter(); public static readonly ISynchronizedViewFilter<T> Null = new NullViewFilter();
public bool IsMatch(T value, TView view) => isMatch(value, view); public bool IsMatch(T value) => isMatch(value);
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(in SynchronizedViewChangedEventArgs<T, TView> eventArgs) => onCollectionChanged?.Invoke(eventArgs);
class NullViewFilter : ISynchronizedViewFilter<T, TView> class NullViewFilter : ISynchronizedViewFilter<T>
{ {
public bool IsMatch(T value, TView view) => true; public bool IsMatch(T value) => true;
public void WhenFalse(T value, TView view) { }
public void WhenTrue(T value, TView view) { }
public void OnCollectionChanged(in SynchronizedViewChangedEventArgs<T, TView> eventArgs) { }
} }
} }
public static class SynchronizedViewFilterExtensions public static class SynchronizedViewFilterExtensions
{ {
public static void AttachFilter<T, TView>(this ISynchronizedView<T, TView> source, Func<T, TView, bool> filter) public static void AttachFilter<T, TView>(this ISynchronizedView<T, TView> source, Func<T, bool> filter)
{ {
source.AttachFilter(new SynchronizedViewFilter<T, TView>(filter, null, null, null)); source.AttachFilter(new SynchronizedViewFilter<T>(filter));
} }
public static void AttachFilter<T, TView>(this ISynchronizedView<T, TView> source, Func<T, TView, bool> isMatch, Action<T, TView>? whenTrue, Action<T, TView>? whenFalse) public static bool IsNullFilter<T>(this ISynchronizedViewFilter<T> filter)
{ {
source.AttachFilter(new SynchronizedViewFilter<T, TView>(isMatch, whenTrue, whenFalse, null)); return filter == SynchronizedViewFilter<T>.Null;
} }
public static void AttachFilter<T, TView>(this ISynchronizedView<T, TView> source, Func<T, TView, bool> isMatch, Action<T, TView>? whenTrue, Action<T, TView>? whenFalse, Action<SynchronizedViewChangedEventArgs<T, TView>>? onCollectionChanged) internal static void InvokeOnAdd<T, TView>(this ISynchronizedView<T, TView> collection, ref int filteredCount, Action<SynchronizedViewChangedEventArgs<T, TView>>? ev, (T value, TView view) value, int index)
{ {
source.AttachFilter(new SynchronizedViewFilter<T, TView>(isMatch, whenTrue, whenFalse, onCollectionChanged)); InvokeOnAdd(collection, ref filteredCount, ev, value.value, value.view, index);
} }
public static bool IsNullFilter<T, TView>(this ISynchronizedViewFilter<T, TView> filter) internal static void InvokeOnAdd<T, TView>(this ISynchronizedView<T, TView> collection, ref int filteredCount, Action<SynchronizedViewChangedEventArgs<T, TView>>? ev, T value, TView view, int index)
{ {
return filter == SynchronizedViewFilter<T, TView>.Null; var isMatch = collection.CurrentFilter.IsMatch(value);
} if (isMatch)
internal static void InvokeOnAdd<T, TView>(this ISynchronizedViewFilter<T, TView> filter, (T value, TView view) value, int index)
{
filter.InvokeOnAdd(value.value, value.view, index);
}
internal static void InvokeOnAdd<T, TView>(this ISynchronizedViewFilter<T, TView> filter, T value, TView view, int index)
{
if (filter.IsMatch(value, view))
{ {
filter.WhenTrue(value, view); filteredCount++;
if (ev != null)
{
ev.Invoke(new SynchronizedViewChangedEventArgs<T, TView>(NotifyCollectionChangedAction.Add, newValue: value, newView: view, newViewIndex: index));
}
} }
else }
internal static void InvokeOnRemove<T, TView>(this ISynchronizedView<T, TView> collection, ref int filteredCount, Action<SynchronizedViewChangedEventArgs<T, TView>>? ev, (T value, TView view) value, int oldIndex)
{
InvokeOnRemove(collection, ref filteredCount, ev, value.value, value.view, oldIndex);
}
internal static void InvokeOnRemove<T, TView>(this ISynchronizedView<T, TView> collection, ref int filteredCount, Action<SynchronizedViewChangedEventArgs<T, TView>>? ev, T value, TView view, int oldIndex)
{
var isMatch = collection.CurrentFilter.IsMatch(value);
if (isMatch)
{ {
filter.WhenFalse(value, view); filteredCount--;
if (ev != null)
{
ev.Invoke(new SynchronizedViewChangedEventArgs<T, TView>(NotifyCollectionChangedAction.Remove, isMatch, oldValue: value, oldView: view, oldViewIndex: oldIndex));
}
} }
filter.OnCollectionChanged(new SynchronizedViewChangedEventArgs<T, TView>(NotifyCollectionChangedAction.Add, newValue: value, newView: view, newViewIndex: index));
} }
internal static void InvokeOnRemove<T, TView>(this ISynchronizedViewFilter<T, TView> filter, (T value, TView view) value, int oldIndex) internal static void InvokeOnMove<T, TView>(this ISynchronizedView<T, TView> collection, ref int filteredCount, Action<SynchronizedViewChangedEventArgs<T, TView>>? ev, (T value, TView view) value, int index, int oldIndex)
{ {
filter.InvokeOnRemove(value.value, value.view, oldIndex); InvokeOnMove(collection, ref filteredCount, ev, value.value, value.view, index, oldIndex);
} }
internal static void InvokeOnRemove<T, TView>(this ISynchronizedViewFilter<T, TView> filter, T value, TView view, int oldIndex) internal static void InvokeOnMove<T, TView>(this ISynchronizedView<T, TView> collection, ref int filteredCount, Action<SynchronizedViewChangedEventArgs<T, TView>>? ev, T value, TView view, int index, int oldIndex)
{ {
filter.OnCollectionChanged(new SynchronizedViewChangedEventArgs<T, TView>(NotifyCollectionChangedAction.Remove, oldValue: value, oldView: view, oldViewIndex: oldIndex)); if (ev != null)
{
// move does not changes filtered-count
var isMatch = collection.CurrentFilter.IsMatch(value);
if (isMatch)
{
ev.Invoke(new SynchronizedViewChangedEventArgs<T, TView>(NotifyCollectionChangedAction.Move, newValue: value, newView: view, newViewIndex: index, oldViewIndex: oldIndex));
}
}
} }
internal static void InvokeOnMove<T, TView>(this ISynchronizedViewFilter<T, TView> filter, (T value, TView view) value, int index, int oldIndex) internal static void InvokeOnReplace<T, TView>(this ISynchronizedView<T, TView> collection, ref int filteredCount, Action<SynchronizedViewChangedEventArgs<T, TView>>? ev, (T value, TView view) value, (T value, TView view) oldValue, int index, int oldIndex = -1)
{ {
InvokeOnMove(filter, value.value, value.view, index, oldIndex); InvokeOnReplace(collection, ref filteredCount, ev, value.value, value.view, oldValue.value, oldValue.view, index, oldIndex);
} }
internal static void InvokeOnMove<T, TView>(this ISynchronizedViewFilter<T, TView> filter, T value, TView view, int index, int oldIndex) internal static void InvokeOnReplace<T, TView>(this ISynchronizedView<T, TView> collection, ref int filteredCount, Action<SynchronizedViewChangedEventArgs<T, TView>>? ev, T value, TView view, T oldValue, TView oldView, int index, int oldIndex = -1)
{ {
filter.OnCollectionChanged(new SynchronizedViewChangedEventArgs<T, TView>(NotifyCollectionChangedAction.Move, newValue: value, newView: view, newViewIndex: index, oldViewIndex: oldIndex)); 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<T, TView>(NotifyCollectionChangedAction.Replace, isMatch, newValue: value, newView: view, oldValue: oldValue, oldView: oldView, newViewIndex: index, oldViewIndex: oldIndex >= 0 ? oldIndex : index));
}
} }
internal static void InvokeOnReplace<T, TView>(this ISynchronizedViewFilter<T, TView> filter, (T value, TView view) value, (T value, TView view) oldValue, int index, int oldIndex = -1) internal static void InvokeOnReset<T, TView>(this ISynchronizedView<T, TView> collection, ref int filteredCount, Action<SynchronizedViewChangedEventArgs<T, TView>>? ev)
{ {
filter.InvokeOnReplace(value.value, value.view, oldValue.value, oldValue.view, index, oldIndex); filteredCount = 0;
} if (ev != null)
{
internal static void InvokeOnReplace<T, TView>(this ISynchronizedViewFilter<T, TView> filter, T value, TView view, T oldValue, TView oldView, int index, int oldIndex = -1) ev.Invoke(new SynchronizedViewChangedEventArgs<T, TView>(NotifyCollectionChangedAction.Reset, true));
{ }
filter.OnCollectionChanged(new SynchronizedViewChangedEventArgs<T, TView>(NotifyCollectionChangedAction.Replace, newValue: value, newView: view, oldValue: oldValue, oldView: oldView, newViewIndex: index, oldViewIndex: oldIndex >= 0 ? oldIndex : index));
}
internal static void InvokeOnReset<T, TView>(this ISynchronizedViewFilter<T, TView> filter)
{
filter.OnCollectionChanged(new SynchronizedViewChangedEventArgs<T, TView>(NotifyCollectionChangedAction.Reset));
} }
internal static void InvokeOnAttach<T, TView>(this ISynchronizedViewFilter<T, TView> filter, T value, TView view) internal static void InvokeOnAttach<T, TView>(this ISynchronizedViewFilter<T, TView> filter, T value, TView view)
@ -139,10 +158,5 @@ namespace ObservableCollections
filter.WhenFalse(value, view); filter.WhenFalse(value, view);
} }
} }
internal static bool IsMatch<T, TView>(this ISynchronizedViewFilter<T, TView> filter, (T, TView) value)
{
return filter.IsMatch(value);
}
} }
} }

View File

@ -1,5 +1,4 @@
using System; using System;
using System.Buffers;
using System.Collections; using System.Collections;
using System.Collections.Generic; using System.Collections.Generic;
using System.Collections.Specialized; using System.Collections.Specialized;
@ -16,6 +15,7 @@ namespace ObservableCollections.Internal
public SynchronizedViewList(ISynchronizedView<T, TView> parent) public SynchronizedViewList(ISynchronizedView<T, TView> parent)
{ {
this.parent = parent; this.parent = parent;
this.listView = parent.Select(x => x.View).ToList(); // need lock
// TODO:add // TODO:add
parent.ViewChanged += Parent_ViewChanged; parent.ViewChanged += Parent_ViewChanged;
} }
@ -24,7 +24,6 @@ namespace ObservableCollections.Internal
{ {
// event is called inside lock(parent.SyncRoot) // event is called inside lock(parent.SyncRoot)
// TODO: invoke in ICollectionEventDispatcher? // TODO: invoke in ICollectionEventDispatcher?
switch (e.Action) switch (e.Action)
{ {
case NotifyCollectionChangedAction.Add: // Add or Insert case NotifyCollectionChangedAction.Add: // Add or Insert
@ -50,6 +49,8 @@ namespace ObservableCollections.Internal
case NotifyCollectionChangedAction.Replace: // Indexer case NotifyCollectionChangedAction.Replace: // Indexer
if (e.NewViewIndex == -1) if (e.NewViewIndex == -1)
{ {
var index = listView.IndexOf(e.OldView);
listView[index] = e.NewView;
} }
else else
{ {
@ -60,6 +61,7 @@ namespace ObservableCollections.Internal
case NotifyCollectionChangedAction.Move: //Remove and Insert case NotifyCollectionChangedAction.Move: //Remove and Insert
if (e.NewViewIndex == -1) if (e.NewViewIndex == -1)
{ {
// do nothing
} }
else else
{ {
@ -68,32 +70,31 @@ namespace ObservableCollections.Internal
} }
break; break;
case NotifyCollectionChangedAction.Reset: // Clear case NotifyCollectionChangedAction.Reset: // Clear
listView.Clear();
break; break;
default: default:
break; break;
} }
// throw new NotImplementedException();
} }
public TView this[int index] => throw new NotImplementedException(); public TView this[int index] => listView[index];
public int Count => throw new NotImplementedException(); public int Count => listView.Count;
public void Dispose()
{
throw new NotImplementedException();
}
public IEnumerator<TView> GetEnumerator() public IEnumerator<TView> GetEnumerator()
{ {
throw new NotImplementedException(); return listView.GetEnumerator();
} }
IEnumerator IEnumerable.GetEnumerator() IEnumerator IEnumerable.GetEnumerator()
{ {
throw new NotImplementedException(); return listView.GetEnumerator();
}
public void Dispose()
{
parent.ViewChanged -= Parent_ViewChanged;
} }
} }

View File

@ -32,7 +32,8 @@ namespace ObservableCollections
ISynchronizedViewFilter<T, TView> filter; ISynchronizedViewFilter<T, TView> filter;
public event NotifyCollectionChangedEventHandler<T>? RoutingCollectionChanged; public event Action<SynchronizedViewChangedEventArgs<T, TView>>? ViewChanged;
// public event NotifyCollectionChangedEventHandler<T>? RoutingCollectionChanged;
public event Action<NotifyCollectionChangedAction>? CollectionStateChanged; public event Action<NotifyCollectionChangedAction>? CollectionStateChanged;
public object SyncRoot { get; } public object SyncRoot { get; }
@ -162,11 +163,7 @@ namespace ObservableCollections
{ {
var v = (e.NewItem, selector(e.NewItem)); var v = (e.NewItem, selector(e.NewItem));
list.Add(v); list.Add(v);
if (filter.IsMatch(v)) this.InvokeOnAdd(ref filteredCount, ViewChanged, v, e.NewStartingIndex);
{
filteredCount++;
}
filter.InvokeOnAdd(v, e.NewStartingIndex);
} }
else else
{ {