Attempt to fix some wonky selection bugs
This commit is contained in:
@@ -5,7 +5,7 @@
|
||||
<Nullable>enable</Nullable>
|
||||
</PropertyGroup>
|
||||
<ItemGroup>
|
||||
<PackageReference Include="Avalonia" Version="11.2.999-cibuild0048140-alpha" />
|
||||
<PackageReference Include="Avalonia" Version="11.1.0-beta2" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\Toolkit.Foundation\Toolkit.Foundation.csproj" />
|
||||
|
||||
+56
-32
@@ -2,7 +2,7 @@
|
||||
|
||||
namespace Toolkit.Foundation;
|
||||
|
||||
public class Cache<TValue>(IComparer<TValue>? comparer = default) :
|
||||
public class Cache<TValue>(IComparer<TValue> comparer) :
|
||||
ICache<TValue>
|
||||
{
|
||||
private readonly List<TValue> items = [];
|
||||
@@ -15,15 +15,23 @@ public class Cache<TValue>(IComparer<TValue>? comparer = default) :
|
||||
|
||||
public void Add(TValue item)
|
||||
{
|
||||
int index = items.BinarySearch(item, comparer);
|
||||
if (index < 0)
|
||||
{
|
||||
index = ~index;
|
||||
}
|
||||
int index = FindInsertIndex(item);
|
||||
items.Insert(index, item);
|
||||
}
|
||||
|
||||
public void Clear() => items.Clear();
|
||||
public void Clear() =>
|
||||
items.Clear();
|
||||
|
||||
public bool Contains(TValue item)
|
||||
{
|
||||
int index = items.IndexOf(item);
|
||||
if (index >= 0)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
public IEnumerator<TValue> GetEnumerator() =>
|
||||
items.GetEnumerator();
|
||||
@@ -31,33 +39,23 @@ public class Cache<TValue>(IComparer<TValue>? comparer = default) :
|
||||
IEnumerator IEnumerable.GetEnumerator() => items.GetEnumerator();
|
||||
|
||||
public int IndexOf(TValue item) =>
|
||||
items.BinarySearch(item, comparer);
|
||||
items.IndexOf(item);
|
||||
|
||||
public bool Remove(TValue item)
|
||||
{
|
||||
int index = items.BinarySearch(item, comparer);
|
||||
int index = items.IndexOf(item);
|
||||
if (index >= 0)
|
||||
{
|
||||
items.RemoveAt(index);
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
public bool Contains(TValue key)
|
||||
{
|
||||
int index = items.BinarySearch(key, comparer);
|
||||
if (index >= 0)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
public bool TryGetValue(TValue key, out TValue? item)
|
||||
{
|
||||
int index = items.BinarySearch(key, comparer);
|
||||
int index = items.IndexOf(key);
|
||||
if (index >= 0)
|
||||
{
|
||||
item = items[index];
|
||||
@@ -67,6 +65,33 @@ public class Cache<TValue>(IComparer<TValue>? comparer = default) :
|
||||
item = default;
|
||||
return false;
|
||||
}
|
||||
|
||||
private int FindInsertIndex(TValue item)
|
||||
{
|
||||
int low = 0, high = items.Count - 1;
|
||||
|
||||
while (low <= high)
|
||||
{
|
||||
int mid = (low + high) / 2;
|
||||
int cmp = comparer.Compare(items[mid], item);
|
||||
|
||||
if (cmp < 0)
|
||||
{
|
||||
low = mid + 1;
|
||||
}
|
||||
else
|
||||
{
|
||||
high = mid - 1;
|
||||
}
|
||||
}
|
||||
|
||||
while (low < items.Count && comparer.Compare(items[low], item) == 0)
|
||||
{
|
||||
low++;
|
||||
}
|
||||
|
||||
return low;
|
||||
}
|
||||
}
|
||||
|
||||
public class Cache<TKey, TValue>(IComparer<TKey> comparer) :
|
||||
@@ -123,6 +148,17 @@ public class Cache<TKey, TValue>(IComparer<TKey> comparer) :
|
||||
|
||||
public void Clear() => items.Clear();
|
||||
|
||||
public bool Contains(TKey key)
|
||||
{
|
||||
int index = items.FindIndex(kvp => comparer.Compare(kvp.Key, key) == 0);
|
||||
if (index >= 0)
|
||||
{
|
||||
items.RemoveAt(index);
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
public IEnumerator<KeyValuePair<TKey, TValue>> GetEnumerator() =>
|
||||
items.GetEnumerator();
|
||||
|
||||
@@ -157,18 +193,6 @@ public class Cache<TKey, TValue>(IComparer<TKey> comparer) :
|
||||
value = default;
|
||||
return false;
|
||||
}
|
||||
|
||||
public bool Contains(TKey key)
|
||||
{
|
||||
int index = items.FindIndex(kvp => comparer.Compare(kvp.Key, key) == 0);
|
||||
if (index >= 0)
|
||||
{
|
||||
items.RemoveAt(index);
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
private class KeyValuePairComparer<TK, TV>(IComparer<TK> comparer) :
|
||||
IComparer<KeyValuePair<TK, TV>>
|
||||
{
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
using CommunityToolkit.Mvvm.ComponentModel;
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
using System.Collections;
|
||||
using System.Collections.Specialized;
|
||||
using System.Reactive.Disposables;
|
||||
@@ -36,17 +35,18 @@ public partial class ObservableCollection<TItem> :
|
||||
{
|
||||
private readonly System.Collections.ObjectModel.ObservableCollection<TItem> collection = [];
|
||||
|
||||
private bool clearing;
|
||||
private readonly Queue<object> pendingEvents = [];
|
||||
[ObservableProperty]
|
||||
private bool activated;
|
||||
|
||||
private bool clearing;
|
||||
[ObservableProperty]
|
||||
private bool initialized;
|
||||
|
||||
[ObservableProperty]
|
||||
private int selectedIndex = 0;
|
||||
|
||||
[ObservableProperty]
|
||||
private TItem? selectedItem;
|
||||
|
||||
private bool supressSelection;
|
||||
public ObservableCollection(IServiceProvider provider,
|
||||
IServiceFactory factory,
|
||||
IMediator mediator,
|
||||
@@ -242,6 +242,8 @@ public partial class ObservableCollection<TItem> :
|
||||
((IEnumerable)collection).GetEnumerator();
|
||||
|
||||
public Task Handle(RemoveEventArgs<TItem> args)
|
||||
{
|
||||
if (Activated)
|
||||
{
|
||||
foreach (TItem item in this.ToList())
|
||||
{
|
||||
@@ -250,71 +252,124 @@ public partial class ObservableCollection<TItem> :
|
||||
Remove(item);
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
pendingEvents.Enqueue(args);
|
||||
}
|
||||
|
||||
return Task.CompletedTask;
|
||||
}
|
||||
|
||||
public Task Handle(CreateEventArgs<TItem> args)
|
||||
{
|
||||
if (Activated)
|
||||
{
|
||||
if (args.Value is TItem item)
|
||||
{
|
||||
Add(item);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
pendingEvents.Enqueue(args);
|
||||
}
|
||||
|
||||
return Task.CompletedTask;
|
||||
}
|
||||
|
||||
public Task Handle(InsertEventArgs<TItem> args)
|
||||
{
|
||||
if (Activated)
|
||||
{
|
||||
if (args.Value is TItem item)
|
||||
{
|
||||
Insert(args.Index, item);
|
||||
if (item is ISelectable newSelection)
|
||||
{
|
||||
if (newSelection.Selected)
|
||||
{
|
||||
foreach (ISelectable oldSelection in this.OfType<ISelectable>().Where(x => x is
|
||||
ISelectable oldSelection && oldSelection != newSelection))
|
||||
{
|
||||
oldSelection.Selected = false;
|
||||
}
|
||||
|
||||
if (item is ISelectable selectable)
|
||||
{
|
||||
if (selectable.Selected)
|
||||
{
|
||||
SelectedItem = item;
|
||||
SelectedIndex = IndexOf(item);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
pendingEvents.Enqueue(args);
|
||||
}
|
||||
|
||||
return Task.CompletedTask;
|
||||
}
|
||||
|
||||
public Task Handle(MoveToEventArgs<TItem> args)
|
||||
{
|
||||
if (Activated)
|
||||
{
|
||||
Move(args.OldIndex, args.NewIndex);
|
||||
}
|
||||
else
|
||||
{
|
||||
pendingEvents.Enqueue(args);
|
||||
}
|
||||
|
||||
return Task.CompletedTask;
|
||||
}
|
||||
|
||||
public Task Handle(MoveEventArgs<TItem> args)
|
||||
{
|
||||
if (Activated)
|
||||
{
|
||||
if (args.Value is TItem item)
|
||||
{
|
||||
Move(args.Index, item);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
pendingEvents.Enqueue(args);
|
||||
}
|
||||
|
||||
return Task.CompletedTask;
|
||||
}
|
||||
|
||||
public Task Handle(ReplaceEventArgs<TItem> args)
|
||||
{
|
||||
if (Activated)
|
||||
{
|
||||
if (args.Value is TItem item)
|
||||
{
|
||||
Replace(args.Index, item);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
pendingEvents.Enqueue(args);
|
||||
}
|
||||
|
||||
return Task.CompletedTask;
|
||||
}
|
||||
|
||||
public Task Handle(RemoveAtEventArgs<TItem> args)
|
||||
{
|
||||
if (Activated)
|
||||
{
|
||||
if (args.Index >= 0 && args.Index <= Count - 1)
|
||||
{
|
||||
RemoveAt(args.Index);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
pendingEvents.Enqueue(args);
|
||||
}
|
||||
|
||||
|
||||
return Task.CompletedTask;
|
||||
}
|
||||
@@ -348,7 +403,7 @@ public partial class ObservableCollection<TItem> :
|
||||
TItem
|
||||
{
|
||||
T? item = Factory.Create<T>(parameters);
|
||||
InsertItem(0, item);
|
||||
InsertItem(index, item);
|
||||
|
||||
return item;
|
||||
}
|
||||
@@ -377,9 +432,7 @@ public partial class ObservableCollection<TItem> :
|
||||
if (item is ISelectable selectable)
|
||||
{
|
||||
selected = selectable.Selected;
|
||||
|
||||
SelectedItem = default;
|
||||
SelectedIndex = -1;
|
||||
}
|
||||
|
||||
RemoveItem(oldIndex);
|
||||
@@ -387,9 +440,7 @@ public partial class ObservableCollection<TItem> :
|
||||
|
||||
if (selected)
|
||||
{
|
||||
SelectedIndex = newIndex;
|
||||
SelectedItem = item;
|
||||
|
||||
if (item is ISelectable selectable2)
|
||||
{
|
||||
selectable2.Selected = true;
|
||||
@@ -413,11 +464,23 @@ public partial class ObservableCollection<TItem> :
|
||||
return true;
|
||||
}
|
||||
|
||||
public virtual Task OnActivated() =>
|
||||
Task.CompletedTask;
|
||||
public virtual Task OnActivated()
|
||||
{
|
||||
Activated = true;
|
||||
while (pendingEvents.Count > 0)
|
||||
{
|
||||
object current = pendingEvents.Dequeue();
|
||||
Handle((dynamic)current);
|
||||
}
|
||||
|
||||
public virtual Task OnDeactivated() =>
|
||||
Task.CompletedTask;
|
||||
return Task.CompletedTask;
|
||||
}
|
||||
|
||||
public virtual Task OnDeactivated()
|
||||
{
|
||||
Activated = false;
|
||||
return Task.CompletedTask;
|
||||
}
|
||||
|
||||
public virtual Task OnDeactivating() =>
|
||||
Task.CompletedTask;
|
||||
@@ -509,20 +572,8 @@ public partial class ObservableCollection<TItem> :
|
||||
private void OnCollectionChanged(object? sender, NotifyCollectionChangedEventArgs args) =>
|
||||
CollectionChanged?.Invoke(this, args);
|
||||
|
||||
partial void OnSelectedIndexChanged(int oldValue, int newValue)
|
||||
{
|
||||
if (oldValue >= 0 && oldValue <= this.Count - 1 && this[oldValue] is ISelectable removed)
|
||||
{
|
||||
removed.Selected = false;
|
||||
}
|
||||
|
||||
if (newValue >= 0 && newValue <= this.Count - 1 && this[newValue] is ISelectable added)
|
||||
{
|
||||
added.Selected = true;
|
||||
}
|
||||
}
|
||||
|
||||
partial void OnSelectedItemChanged(TItem? oldValue, TItem? newValue)
|
||||
partial void OnSelectedItemChanged(TItem? oldValue,
|
||||
TItem? newValue)
|
||||
{
|
||||
if (SelectedItem is not null && !SelectedItem.Equals(oldValue))
|
||||
{
|
||||
|
||||
@@ -5,7 +5,7 @@
|
||||
<Nullable>enable</Nullable>
|
||||
</PropertyGroup>
|
||||
<ItemGroup>
|
||||
<PackageReference Include="Avalonia" Version="11.2.999-cibuild0048140-alpha" />
|
||||
<PackageReference Include="Avalonia" Version="11.1.0-beta2" />
|
||||
<PackageReference Include="Avalonia.Xaml.Behaviors" Version="11.1.0-beta2.1" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
|
||||
@@ -12,7 +12,7 @@
|
||||
<AvaloniaResource Include="Fonts\FluentSystemIcons-Resizable.ttf" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<PackageReference Include="Avalonia" Version="11.2.999-cibuild0048140-alpha" />
|
||||
<PackageReference Include="Avalonia" Version="11.1.0-beta2" />
|
||||
<PackageReference Include="Avalonia.Labs.Controls" Version="11.0.10.1" />
|
||||
<PackageReference Include="FluentAvaloniaUI" Version="2.1.0-preview5" />
|
||||
</ItemGroup>
|
||||
|
||||
Reference in New Issue
Block a user