Attempt to fix some wonky selection bugs

This commit is contained in:
TheXamlGuy
2024-05-28 22:41:35 +01:00
parent 338be9b328
commit e2cf4cdae4
5 changed files with 159 additions and 84 deletions
+1 -1
View File
@@ -5,7 +5,7 @@
<Nullable>enable</Nullable> <Nullable>enable</Nullable>
</PropertyGroup> </PropertyGroup>
<ItemGroup> <ItemGroup>
<PackageReference Include="Avalonia" Version="11.2.999-cibuild0048140-alpha" /> <PackageReference Include="Avalonia" Version="11.1.0-beta2" />
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>
<ProjectReference Include="..\Toolkit.Foundation\Toolkit.Foundation.csproj" /> <ProjectReference Include="..\Toolkit.Foundation\Toolkit.Foundation.csproj" />
+57 -33
View File
@@ -2,7 +2,7 @@
namespace Toolkit.Foundation; namespace Toolkit.Foundation;
public class Cache<TValue>(IComparer<TValue>? comparer = default) : public class Cache<TValue>(IComparer<TValue> comparer) :
ICache<TValue> ICache<TValue>
{ {
private readonly List<TValue> items = []; private readonly List<TValue> items = [];
@@ -15,15 +15,23 @@ public class Cache<TValue>(IComparer<TValue>? comparer = default) :
public void Add(TValue item) public void Add(TValue item)
{ {
int index = items.BinarySearch(item, comparer); int index = FindInsertIndex(item);
if (index < 0)
{
index = ~index;
}
items.Insert(index, 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() => public IEnumerator<TValue> GetEnumerator() =>
items.GetEnumerator(); items.GetEnumerator();
@@ -31,33 +39,23 @@ public class Cache<TValue>(IComparer<TValue>? comparer = default) :
IEnumerator IEnumerable.GetEnumerator() => items.GetEnumerator(); IEnumerator IEnumerable.GetEnumerator() => items.GetEnumerator();
public int IndexOf(TValue item) => public int IndexOf(TValue item) =>
items.BinarySearch(item, comparer); items.IndexOf(item);
public bool Remove(TValue item) public bool Remove(TValue item)
{ {
int index = items.BinarySearch(item, comparer); int index = items.IndexOf(item);
if (index >= 0) if (index >= 0)
{ {
items.RemoveAt(index); items.RemoveAt(index);
return true; return true;
} }
return false;
}
public bool Contains(TValue key)
{
int index = items.BinarySearch(key, comparer);
if (index >= 0)
{
return true;
}
return false; return false;
} }
public bool TryGetValue(TValue key, out TValue? item) public bool TryGetValue(TValue key, out TValue? item)
{ {
int index = items.BinarySearch(key, comparer); int index = items.IndexOf(key);
if (index >= 0) if (index >= 0)
{ {
item = items[index]; item = items[index];
@@ -67,6 +65,33 @@ public class Cache<TValue>(IComparer<TValue>? comparer = default) :
item = default; item = default;
return false; 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) : public class Cache<TKey, TValue>(IComparer<TKey> comparer) :
@@ -123,8 +148,19 @@ public class Cache<TKey, TValue>(IComparer<TKey> comparer) :
public void Clear() => items.Clear(); 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() => public IEnumerator<KeyValuePair<TKey, TValue>> GetEnumerator() =>
items.GetEnumerator(); items.GetEnumerator();
IEnumerator IEnumerable.GetEnumerator() => IEnumerator IEnumerable.GetEnumerator() =>
GetEnumerator(); GetEnumerator();
@@ -157,18 +193,6 @@ public class Cache<TKey, TValue>(IComparer<TKey> comparer) :
value = default; value = default;
return false; 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) : private class KeyValuePairComparer<TK, TV>(IComparer<TK> comparer) :
IComparer<KeyValuePair<TK, TV>> IComparer<KeyValuePair<TK, TV>>
{ {
+99 -48
View File
@@ -1,5 +1,4 @@
using CommunityToolkit.Mvvm.ComponentModel; using CommunityToolkit.Mvvm.ComponentModel;
using Microsoft.Extensions.DependencyInjection;
using System.Collections; using System.Collections;
using System.Collections.Specialized; using System.Collections.Specialized;
using System.Reactive.Disposables; using System.Reactive.Disposables;
@@ -36,17 +35,18 @@ public partial class ObservableCollection<TItem> :
{ {
private readonly System.Collections.ObjectModel.ObservableCollection<TItem> collection = []; private readonly System.Collections.ObjectModel.ObservableCollection<TItem> collection = [];
private bool clearing; private readonly Queue<object> pendingEvents = [];
[ObservableProperty]
private bool activated;
private bool clearing;
[ObservableProperty] [ObservableProperty]
private bool initialized; private bool initialized;
[ObservableProperty]
private int selectedIndex = 0;
[ObservableProperty] [ObservableProperty]
private TItem? selectedItem; private TItem? selectedItem;
private bool supressSelection;
public ObservableCollection(IServiceProvider provider, public ObservableCollection(IServiceProvider provider,
IServiceFactory factory, IServiceFactory factory,
IMediator mediator, IMediator mediator,
@@ -243,22 +243,36 @@ public partial class ObservableCollection<TItem> :
public Task Handle(RemoveEventArgs<TItem> args) public Task Handle(RemoveEventArgs<TItem> args)
{ {
foreach (TItem item in this.ToList()) if (Activated)
{ {
if (args.Value is not null && args.Value.Equals(item)) foreach (TItem item in this.ToList())
{ {
Remove(item); if (args.Value is not null && args.Value.Equals(item))
{
Remove(item);
}
} }
} }
else
{
pendingEvents.Enqueue(args);
}
return Task.CompletedTask; return Task.CompletedTask;
} }
public Task Handle(CreateEventArgs<TItem> args) public Task Handle(CreateEventArgs<TItem> args)
{ {
if (args.Value is TItem item) if (Activated)
{ {
Add(item); if (args.Value is TItem item)
{
Add(item);
}
}
else
{
pendingEvents.Enqueue(args);
} }
return Task.CompletedTask; return Task.CompletedTask;
@@ -266,34 +280,60 @@ public partial class ObservableCollection<TItem> :
public Task Handle(InsertEventArgs<TItem> args) public Task Handle(InsertEventArgs<TItem> args)
{ {
if (args.Value is TItem item) if (Activated)
{ {
Insert(args.Index, item); if (args.Value is TItem item)
if (item is ISelectable selectable)
{ {
if (selectable.Selected) Insert(args.Index, item);
if (item is ISelectable newSelection)
{ {
SelectedItem = item; if (newSelection.Selected)
SelectedIndex = IndexOf(item); {
foreach (ISelectable oldSelection in this.OfType<ISelectable>().Where(x => x is
ISelectable oldSelection && oldSelection != newSelection))
{
oldSelection.Selected = false;
}
SelectedItem = item;
}
} }
} }
} }
else
{
pendingEvents.Enqueue(args);
}
return Task.CompletedTask; return Task.CompletedTask;
} }
public Task Handle(MoveToEventArgs<TItem> args) public Task Handle(MoveToEventArgs<TItem> args)
{ {
Move(args.OldIndex, args.NewIndex); if (Activated)
{
Move(args.OldIndex, args.NewIndex);
}
else
{
pendingEvents.Enqueue(args);
}
return Task.CompletedTask; return Task.CompletedTask;
} }
public Task Handle(MoveEventArgs<TItem> args) public Task Handle(MoveEventArgs<TItem> args)
{ {
if (args.Value is TItem item) if (Activated)
{ {
Move(args.Index, item); if (args.Value is TItem item)
{
Move(args.Index, item);
}
}
else
{
pendingEvents.Enqueue(args);
} }
return Task.CompletedTask; return Task.CompletedTask;
@@ -301,9 +341,16 @@ public partial class ObservableCollection<TItem> :
public Task Handle(ReplaceEventArgs<TItem> args) public Task Handle(ReplaceEventArgs<TItem> args)
{ {
if (args.Value is TItem item) if (Activated)
{ {
Replace(args.Index, item); if (args.Value is TItem item)
{
Replace(args.Index, item);
}
}
else
{
pendingEvents.Enqueue(args);
} }
return Task.CompletedTask; return Task.CompletedTask;
@@ -311,10 +358,18 @@ public partial class ObservableCollection<TItem> :
public Task Handle(RemoveAtEventArgs<TItem> args) public Task Handle(RemoveAtEventArgs<TItem> args)
{ {
if (args.Index >= 0 && args.Index <= Count - 1) if (Activated)
{ {
RemoveAt(args.Index); if (args.Index >= 0 && args.Index <= Count - 1)
{
RemoveAt(args.Index);
}
} }
else
{
pendingEvents.Enqueue(args);
}
return Task.CompletedTask; return Task.CompletedTask;
} }
@@ -343,12 +398,12 @@ public partial class ObservableCollection<TItem> :
} }
public TItem Insert<T>(int index = 0, public TItem Insert<T>(int index = 0,
params object?[] parameters) params object?[] parameters)
where T : where T :
TItem TItem
{ {
T? item = Factory.Create<T>(parameters); T? item = Factory.Create<T>(parameters);
InsertItem(0, item); InsertItem(index, item);
return item; return item;
} }
@@ -377,9 +432,7 @@ public partial class ObservableCollection<TItem> :
if (item is ISelectable selectable) if (item is ISelectable selectable)
{ {
selected = selectable.Selected; selected = selectable.Selected;
SelectedItem = default; SelectedItem = default;
SelectedIndex = -1;
} }
RemoveItem(oldIndex); RemoveItem(oldIndex);
@@ -387,9 +440,7 @@ public partial class ObservableCollection<TItem> :
if (selected) if (selected)
{ {
SelectedIndex = newIndex;
SelectedItem = item; SelectedItem = item;
if (item is ISelectable selectable2) if (item is ISelectable selectable2)
{ {
selectable2.Selected = true; selectable2.Selected = true;
@@ -413,11 +464,23 @@ public partial class ObservableCollection<TItem> :
return true; return true;
} }
public virtual Task OnActivated() => public virtual Task OnActivated()
Task.CompletedTask; {
Activated = true;
while (pendingEvents.Count > 0)
{
object current = pendingEvents.Dequeue();
Handle((dynamic)current);
}
public virtual Task OnDeactivated() => return Task.CompletedTask;
Task.CompletedTask; }
public virtual Task OnDeactivated()
{
Activated = false;
return Task.CompletedTask;
}
public virtual Task OnDeactivating() => public virtual Task OnDeactivating() =>
Task.CompletedTask; Task.CompletedTask;
@@ -509,20 +572,8 @@ public partial class ObservableCollection<TItem> :
private void OnCollectionChanged(object? sender, NotifyCollectionChangedEventArgs args) => private void OnCollectionChanged(object? sender, NotifyCollectionChangedEventArgs args) =>
CollectionChanged?.Invoke(this, args); CollectionChanged?.Invoke(this, args);
partial void OnSelectedIndexChanged(int oldValue, int newValue) partial void OnSelectedItemChanged(TItem? oldValue,
{ TItem? 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)
{ {
if (SelectedItem is not null && !SelectedItem.Equals(oldValue)) if (SelectedItem is not null && !SelectedItem.Equals(oldValue))
{ {
@@ -5,7 +5,7 @@
<Nullable>enable</Nullable> <Nullable>enable</Nullable>
</PropertyGroup> </PropertyGroup>
<ItemGroup> <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" /> <PackageReference Include="Avalonia.Xaml.Behaviors" Version="11.1.0-beta2.1" />
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>
@@ -12,7 +12,7 @@
<AvaloniaResource Include="Fonts\FluentSystemIcons-Resizable.ttf" /> <AvaloniaResource Include="Fonts\FluentSystemIcons-Resizable.ttf" />
</ItemGroup> </ItemGroup>
<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="Avalonia.Labs.Controls" Version="11.0.10.1" />
<PackageReference Include="FluentAvaloniaUI" Version="2.1.0-preview5" /> <PackageReference Include="FluentAvaloniaUI" Version="2.1.0-preview5" />
</ItemGroup> </ItemGroup>