Browse Source

Use SelectedItems for change event args.

pull/3470/head
Steven Kirk 6 years ago
parent
commit
9073234f72
  1. 12
      src/Avalonia.Controls/IndexRange.cs
  2. 37
      src/Avalonia.Controls/SelectedItems.cs
  3. 14
      src/Avalonia.Controls/SelectionModel.cs
  4. 149
      src/Avalonia.Controls/SelectionModelChangeSet.cs
  5. 53
      src/Avalonia.Controls/SelectionModelSelectionChangedEventArgs.cs
  6. 32
      src/Avalonia.Controls/SelectionNodeOperation.cs

12
src/Avalonia.Controls/IndexRange.cs

@ -202,6 +202,18 @@ namespace Avalonia.Controls
}
}
public static int GetCount(IEnumerable<IndexRange> ranges)
{
var result = 0;
foreach (var range in ranges)
{
result += (range.End - range.Begin) + 1;
}
return result;
}
private static void MergeRanges(IList<IndexRange> ranges)
{
for (var i = ranges.Count - 2; i >= 0; --i)

37
src/Avalonia.Controls/SelectedItems.cs

@ -6,44 +6,37 @@
using System;
using System.Collections;
using System.Collections.Generic;
using SelectedItemInfo = Avalonia.Controls.SelectionModel.SelectedItemInfo;
#nullable enable
namespace Avalonia.Controls
{
internal class SelectedItems<T> : IReadOnlyList<T>
public interface ISelectedItemInfo
{
private readonly List<SelectedItemInfo> _infos;
private readonly Func<List<SelectedItemInfo>, int, T> _getAtImpl;
public IndexPath Path { get; }
}
internal class SelectedItems<TValue, Tinfo> : IReadOnlyList<TValue>
where Tinfo : ISelectedItemInfo
{
private readonly List<Tinfo> _infos;
private readonly Func<List<Tinfo>, int, TValue> _getAtImpl;
public SelectedItems(
List<SelectedItemInfo> infos,
Func<List<SelectedItemInfo>, int, T> getAtImpl)
List<Tinfo> infos,
int count,
Func<List<Tinfo>, int, TValue> getAtImpl)
{
_infos = infos;
_getAtImpl = getAtImpl;
foreach (var info in infos)
{
var node = info.Node;
if (node != null)
{
Count += node.SelectedCount;
}
else
{
throw new InvalidOperationException("Selection changed after the SelectedIndices/Items property was read.");
}
}
Count = count;
}
public T this[int index] => _getAtImpl(_infos, index);
public TValue this[int index] => _getAtImpl(_infos, index);
public int Count { get; }
public IEnumerator<T> GetEnumerator()
public IEnumerator<TValue> GetEnumerator()
{
for (var i = 0; i < Count; ++i)
{

14
src/Avalonia.Controls/SelectionModel.cs

@ -165,6 +165,7 @@ namespace Avalonia.Controls
if (_selectedItemsCached == null)
{
var selectedInfos = new List<SelectedItemInfo>();
var count = 0;
if (_rootNode.Source != null)
{
@ -176,6 +177,7 @@ namespace Avalonia.Controls
if (currentInfo.Node.SelectedCount > 0)
{
selectedInfos.Add(new SelectedItemInfo(currentInfo.Node, currentInfo.Path));
count += currentInfo.Node.SelectedCount;
}
});
}
@ -185,8 +187,9 @@ namespace Avalonia.Controls
// the selected item at a particular index. This avoid having to create the storage and copying
// needed in a dumb vector. This also allows us to expose a tree of selected nodes into an
// easier to consume flat vector view of objects.
var selectedItems = new SelectedItems<object?> (
var selectedItems = new SelectedItems<object?, SelectedItemInfo> (
selectedInfos,
count,
(infos, index) =>
{
var currentIndex = 0;
@ -233,6 +236,8 @@ namespace Avalonia.Controls
if (_selectedIndicesCached == null)
{
var selectedInfos = new List<SelectedItemInfo>();
var count = 0;
SelectionTreeHelper.Traverse(
_rootNode,
false,
@ -241,6 +246,7 @@ namespace Avalonia.Controls
if (currentInfo.Node.SelectedCount > 0)
{
selectedInfos.Add(new SelectedItemInfo(currentInfo.Node, currentInfo.Path));
count += currentInfo.Node.SelectedCount;
}
});
@ -249,8 +255,9 @@ namespace Avalonia.Controls
// the IndexPath at a particular index. This avoid having to create the storage and copying
// needed in a dumb vector. This also allows us to expose a tree of selected nodes into an
// easier to consume flat vector view of IndexPaths.
var indices = new SelectedItems<IndexPath>(
var indices = new SelectedItems<IndexPath, SelectedItemInfo>(
selectedInfos,
count,
(infos, index) => // callback for GetAt(index)
{
var currentIndex = 0;
@ -720,7 +727,7 @@ namespace Avalonia.Controls
OnSelectionChanged(e);
}
internal class SelectedItemInfo
internal class SelectedItemInfo : ISelectedItemInfo
{
public SelectedItemInfo(SelectionNode node, IndexPath path)
{
@ -730,6 +737,7 @@ namespace Avalonia.Controls
public SelectionNode Node { get; }
public IndexPath Path { get; }
public int Count => Node.SelectedCount;
}
private struct Operation : IDisposable

149
src/Avalonia.Controls/SelectionModelChangeSet.cs

@ -14,61 +14,144 @@ namespace Avalonia.Controls
public SelectionModelSelectionChangedEventArgs CreateEventArgs()
{
var deselectedCount = 0;
var selectedCount = 0;
foreach (var change in _changes)
{
deselectedCount += change.DeselectedCount;
selectedCount += change.SelectedCount;
}
var deselectedIndices = new SelectedItems<IndexPath, SelectionNodeOperation>(
_changes,
deselectedCount,
GetDeselectedIndexAt);
var selectedIndices = new SelectedItems<IndexPath, SelectionNodeOperation>(
_changes,
selectedCount,
GetSelectedIndexAt);
var deselectedItems = new SelectedItems<object, SelectionNodeOperation>(
_changes,
deselectedCount,
GetDeselectedItemAt);
var selectedItems = new SelectedItems<object, SelectionNodeOperation>(
_changes,
selectedCount,
GetSelectedItemAt);
return new SelectionModelSelectionChangedEventArgs(
CreateIndices(x => x.DeselectedRanges),
CreateIndices(x => x.SelectedRanges),
CreateItems(x => x.DeselectedRanges),
CreateItems(x => x.SelectedRanges));
deselectedIndices,
selectedIndices,
deselectedItems,
selectedItems);
}
private IEnumerable<IndexPath> CreateIndices(Func<SelectionNodeOperation, List<IndexRange>?> selector)
private IndexPath GetDeselectedIndexAt(
List<SelectionNodeOperation> infos,
int index)
{
if (_changes == null)
{
yield break;
}
static int GetCount(SelectionNodeOperation info) => info.DeselectedCount;
static List<IndexRange> GetRanges(SelectionNodeOperation info) => info.DeselectedRanges;
return GetIndexAt(infos, index, GetCount, GetRanges);
}
foreach (var i in _changes)
private IndexPath GetSelectedIndexAt(
List<SelectionNodeOperation> infos,
int index)
{
static int GetCount(SelectionNodeOperation info) => info.SelectedCount;
static List<IndexRange> GetRanges(SelectionNodeOperation info) => info.SelectedRanges;
return GetIndexAt(infos, index, GetCount, GetRanges);
}
private object GetDeselectedItemAt(
List<SelectionNodeOperation> infos,
int index)
{
static int GetCount(SelectionNodeOperation info) => info.DeselectedCount;
static List<IndexRange> GetRanges(SelectionNodeOperation info) => info.DeselectedRanges;
return GetItemAt(infos, index, GetCount, GetRanges);
}
private object GetSelectedItemAt(
List<SelectionNodeOperation> infos,
int index)
{
static int GetCount(SelectionNodeOperation info) => info.SelectedCount;
static List<IndexRange> GetRanges(SelectionNodeOperation info) => info.SelectedRanges;
return GetItemAt(infos, index, GetCount, GetRanges);
}
private IndexPath GetIndexAt(
List<SelectionNodeOperation> infos,
int index,
Func<SelectionNodeOperation, int> getCount,
Func<SelectionNodeOperation, List<IndexRange>> getRanges)
{
var currentIndex = 0;
IndexPath path = default;
foreach (var info in infos)
{
var ranges = selector(i);
var currentCount = getCount(info);
if (ranges != null)
if (index >= currentIndex && index < currentIndex + currentCount)
{
foreach (var j in ranges)
{
for (var k = j.Begin; k <= j.End; ++k)
{
yield return i.Path.CloneWithChildIndex(k);
}
}
int targetIndex = GetIndexAt(getRanges(info), index - currentIndex);
path = info.Path.CloneWithChildIndex(targetIndex);
break;
}
currentIndex += currentCount;
}
return path;
}
private IEnumerable<object> CreateItems(Func<SelectionNodeOperation, List<IndexRange>?> selector)
private object GetItemAt(
List<SelectionNodeOperation> infos,
int index,
Func<SelectionNodeOperation, int> getCount,
Func<SelectionNodeOperation, List<IndexRange>> getRanges)
{
if (_changes == null)
var currentIndex = 0;
object item = null;
foreach (var info in infos)
{
yield break;
var currentCount = getCount(info);
if (index >= currentIndex && index < currentIndex + currentCount)
{
int targetIndex = GetIndexAt(getRanges(info), index - currentIndex);
item = info.Items.GetAt(targetIndex);
break;
}
currentIndex += currentCount;
}
var result = new List<object>();
return item;
}
foreach (var i in _changes)
private int GetIndexAt(List<IndexRange> ranges, int index)
{
var currentIndex = 0;
foreach (var range in ranges)
{
var ranges = selector(i);
var currentCount = (range.End - range.Begin) + 1;
if (ranges != null && i.Items != null)
if (index >= currentIndex && index < currentIndex + currentCount)
{
foreach (var j in ranges)
{
for (var k = j.Begin; k <= j.End; ++k)
{
yield return i.Items.GetAt(k);
}
}
return range.Begin + (index - currentIndex);
}
currentIndex += currentCount;
}
throw new IndexOutOfRangeException();
}
}
}

53
src/Avalonia.Controls/SelectionModelSelectionChangedEventArgs.cs

@ -12,73 +12,36 @@ namespace Avalonia.Controls
{
public class SelectionModelSelectionChangedEventArgs : EventArgs
{
private readonly IEnumerable<IndexPath>? _deselectedIndicesSource;
private readonly IEnumerable<IndexPath>? _selectedIndicesSource;
private readonly IEnumerable<object>? _deselectedItemsSource;
private readonly IEnumerable<object>? _selectedItemsSource;
private IReadOnlyList<IndexPath>? _deselectedIndices;
private IReadOnlyList<IndexPath>? _selectedIndices;
private IReadOnlyList<object>? _deselectedItems;
private IReadOnlyList<object>? _selectedItems;
public SelectionModelSelectionChangedEventArgs(
IReadOnlyList<IndexPath>? deselectedIndices,
IReadOnlyList<IndexPath>? selectedIndices,
IReadOnlyList<object>? deselectedItems,
IReadOnlyList<object>? selectedItems)
{
_deselectedIndices = deselectedIndices ?? Array.Empty<IndexPath>();
_selectedIndices = selectedIndices ?? Array.Empty<IndexPath>();
_deselectedItems = deselectedItems ?? Array.Empty<object>();
_selectedItems= selectedItems ?? Array.Empty<object>();
}
public SelectionModelSelectionChangedEventArgs(
IEnumerable<IndexPath>? deselectedIndices,
IEnumerable<IndexPath>? selectedIndices,
IEnumerable<object>? deselectedItems,
IEnumerable<object>? selectedItems)
{
static void Set<T>(IEnumerable<T>? source, ref IEnumerable<T>? sourceField, ref IReadOnlyList<T>? field)
{
if (source != null)
{
sourceField = source;
}
else
{
field = Array.Empty<T>();
}
}
Set(deselectedIndices, ref _deselectedIndicesSource, ref _deselectedIndices);
Set(selectedIndices, ref _selectedIndicesSource, ref _selectedIndices);
Set(deselectedItems, ref _deselectedItemsSource, ref _deselectedItems);
Set(selectedItems, ref _selectedItemsSource, ref _selectedItems);
DeselectedIndices = deselectedIndices ?? Array.Empty<IndexPath>();
SelectedIndices = selectedIndices ?? Array.Empty<IndexPath>();
DeselectedItems = deselectedItems ?? Array.Empty<object>();
SelectedItems= selectedItems ?? Array.Empty<object>();
}
/// <summary>
/// Gets the indices of the items that were removed from the selection.
/// </summary>
public IReadOnlyList<IndexPath> DeselectedIndices
=> _deselectedIndices ??= new List<IndexPath>(_deselectedIndicesSource);
public IReadOnlyList<IndexPath> DeselectedIndices { get; }
/// <summary>
/// Gets the indices of the items that were added to the selection.
/// </summary>
public IReadOnlyList<IndexPath> SelectedIndices
=> _selectedIndices ??= new List<IndexPath>(_selectedIndicesSource);
public IReadOnlyList<IndexPath> SelectedIndices { get; }
/// <summary>
/// Gets the items that were removed from the selection.
/// </summary>
public IReadOnlyList<object> DeselectedItems
=> _deselectedItems ??= new List<object>(_deselectedItemsSource);
public IReadOnlyList<object> DeselectedItems { get; }
/// <summary>
/// Gets the items that were added to the selection.
/// </summary>
public IReadOnlyList<object> SelectedItems
=> _selectedItems ??= new List<object>(_selectedItemsSource);
public IReadOnlyList<object> SelectedItems { get; }
}
}

32
src/Avalonia.Controls/SelectionNodeOperation.cs

@ -6,11 +6,13 @@ using System.Linq;
namespace Avalonia.Controls
{
internal class SelectionNodeOperation
internal class SelectionNodeOperation : ISelectedItemInfo
{
private readonly SelectionNode _owner;
private List<IndexRange>? _selected;
private List<IndexRange>? _deselected;
private int _selectedCount = -1;
private int _deselectedCount = -1;
public SelectionNodeOperation(SelectionNode owner)
{
@ -23,9 +25,36 @@ namespace Avalonia.Controls
public IndexPath Path => _owner.IndexPath;
public ItemsSourceView? Items => _owner.ItemsSourceView;
public int SelectedCount
{
get
{
if (_selectedCount == -1)
{
_selectedCount = (_selected != null) ? IndexRange.GetCount(_selected) : 0;
}
return _selectedCount;
}
}
public int DeselectedCount
{
get
{
if (_deselectedCount == -1)
{
_deselectedCount = (_deselected != null) ? IndexRange.GetCount(_deselected) : 0;
}
return _deselectedCount;
}
}
public void Selected(IndexRange range)
{
Add(range, ref _selected, _deselected);
_selectedCount = -1;
}
public void Selected(IEnumerable<IndexRange> ranges)
@ -39,6 +68,7 @@ namespace Avalonia.Controls
public void Deselected(IndexRange range)
{
Add(range, ref _deselected, _selected);
_deselectedCount = -1;
}
public void Deselected(IEnumerable<IndexRange> ranges)

Loading…
Cancel
Save