diff --git a/src/Avalonia.Controls/SelectionModel.cs b/src/Avalonia.Controls/SelectionModel.cs index e8958438f1..dd4934f9e5 100644 --- a/src/Avalonia.Controls/SelectionModel.cs +++ b/src/Avalonia.Controls/SelectionModel.cs @@ -774,7 +774,10 @@ namespace Avalonia.Controls winrtEnd, info => { - info.ParentNode!.Select(info.Path.GetAt(info.Path.GetSize() - 1), select); + if (info.Path >= winrtStart && info.Path <= winrtEnd) + { + info.ParentNode!.Select(info.Path.GetAt(info.Path.GetSize() - 1), select); + } }); } diff --git a/tests/Avalonia.Controls.UnitTests/SelectionModelTests.cs b/tests/Avalonia.Controls.UnitTests/SelectionModelTests.cs index 337c0e6c5f..cea41526ad 100644 --- a/tests/Avalonia.Controls.UnitTests/SelectionModelTests.cs +++ b/tests/Avalonia.Controls.UnitTests/SelectionModelTests.cs @@ -34,9 +34,9 @@ namespace Avalonia.Controls.UnitTests SelectionModel selectionModel = new SelectionModel() { SingleSelect = true }; _output.WriteLine("No source set."); Select(selectionModel, 4, true); - ValidateSelection(selectionModel, new List() { Path(4) }); + ValidateSelection(selectionModel, Path(4)); Select(selectionModel, 4, false); - ValidateSelection(selectionModel, new List() { }); + ValidateSelection(selectionModel); } [Fact] @@ -46,9 +46,9 @@ namespace Avalonia.Controls.UnitTests _output.WriteLine("Set the source to 10 items"); selectionModel.Source = Enumerable.Range(0, 10).ToList(); Select(selectionModel, 3, true); - ValidateSelection(selectionModel, new List() { Path(3) }, new List() { Path() }); + ValidateSelection(selectionModel, Path(3)); Select(selectionModel, 3, false); - ValidateSelection(selectionModel, new List() { }); + ValidateSelection(selectionModel); } [Fact] @@ -61,11 +61,11 @@ namespace Avalonia.Controls.UnitTests selectionModel.SelectionChanged += delegate (object sender, SelectionModelSelectionChangedEventArgs args) { selectionChangedFiredCount++; - ValidateSelection(selectionModel, new List() { Path(4) }, new List() { Path() }); + ValidateSelection(selectionModel, Path(4)); }; Select(selectionModel, 4, true); - ValidateSelection(selectionModel, new List() { Path(4) }, new List() { Path() }); + ValidateSelection(selectionModel, Path(4)); Assert.Equal(1, selectionChangedFiredCount); } @@ -85,41 +85,29 @@ namespace Avalonia.Controls.UnitTests selectionModel.Source = Enumerable.Range(0, 10).ToList(); Select(selectionModel, 4, true); - ValidateSelection(selectionModel, new List() { Path(4) }, new List() { Path() }); + ValidateSelection(selectionModel, Path(4)); SelectRangeFromAnchor(selectionModel, 8, true /* select */); ValidateSelection(selectionModel, - new List() - { - Path(4), - Path(5), - Path(6), - Path(7), - Path(8) - }, - new List() { Path() }); + Path(4), + Path(5), + Path(6), + Path(7), + Path(8)); ClearSelection(selectionModel); SetAnchorIndex(selectionModel, 6); SelectRangeFromAnchor(selectionModel, 3, true /* select */); ValidateSelection(selectionModel, - new List() - { - Path(3), - Path(4), - Path(5), - Path(6) - }, - new List() { Path() }); + Path(3), + Path(4), + Path(5), + Path(6)); SetAnchorIndex(selectionModel, 4); SelectRangeFromAnchor(selectionModel, 5, false /* select */); ValidateSelection(selectionModel, - new List() - { - Path(3), - Path(6) - }, - new List() { Path() }); + Path(3), + Path(6)); } [Fact] @@ -129,10 +117,9 @@ namespace Avalonia.Controls.UnitTests _output.WriteLine("Setting the source"); selectionModel.Source = CreateNestedData(1 /* levels */ , 2 /* groupsAtLevel */, 2 /* countAtLeaf */); Select(selectionModel, 1, 1, true); - ValidateSelection(selectionModel, - new List() { Path(1, 1) }, new List() { Path(), Path(1) }); + ValidateSelection(selectionModel, Path(1, 1)); Select(selectionModel, 1, 1, false); - ValidateSelection(selectionModel, new List() { }); + ValidateSelection(selectionModel); } [Fact] @@ -143,68 +130,36 @@ namespace Avalonia.Controls.UnitTests selectionModel.Source = CreateNestedData(1 /* levels */ , 3 /* groupsAtLevel */, 3 /* countAtLeaf */); Select(selectionModel, 1, 2, true); - ValidateSelection(selectionModel, new List() { Path(1, 2) }, new List() { Path(), Path(1) }); + ValidateSelection(selectionModel, Path(1, 2)); SelectRangeFromAnchor(selectionModel, 2, 2, true /* select */); ValidateSelection(selectionModel, - new List() - { - Path(1, 2), - Path(2), // Inner node should be selected since everything 2.* is selected - Path(2, 0), - Path(2, 1), - Path(2, 2) - }, - new List() - { - Path(), - Path(1) - }, - 1 /* selectedInnerNodes */); + Path(1, 2), + Path(2, 0), + Path(2, 1), + Path(2, 2)); ClearSelection(selectionModel); SetAnchorIndex(selectionModel, 2, 1); SelectRangeFromAnchor(selectionModel, 0, 1, true /* select */); ValidateSelection(selectionModel, - new List() - { - Path(0, 1), - Path(0, 2), - Path(1, 0), - Path(1, 1), - Path(1, 2), - Path(1), - Path(2, 0), - Path(2, 1) - }, - new List() - { - Path(), - Path(0), - Path(2), - }, - 1 /* selectedInnerNodes */); + Path(0, 1), + Path(0, 2), + Path(1, 0), + Path(1, 1), + Path(1, 2), + Path(2, 0), + Path(2, 1)); SetAnchorIndex(selectionModel, 1, 1); SelectRangeFromAnchor(selectionModel, 2, 0, false /* select */); ValidateSelection(selectionModel, - new List() - { - Path(0, 1), - Path(0, 2), - Path(1, 0), - Path(2, 1) - }, - new List() - { - Path(), - Path(1), - Path(0), - Path(2), - }, - 0 /* selectedInnerNodes */); + Path(0, 1), + Path(0, 2), + Path(1, 0), + Path(2, 1)); ClearSelection(selectionModel); - ValidateSelection(selectionModel, new List() { }); + ValidateSelection(selectionModel); } [Fact] @@ -215,30 +170,11 @@ namespace Avalonia.Controls.UnitTests selectionModel.Source = CreateNestedData(3 /* levels */ , 2 /* groupsAtLevel */, 2 /* countAtLeaf */); var path = Path(1, 0, 1, 1); Select(selectionModel, path, true); - ValidateSelection(selectionModel, - new List() { path }, - new List() - { - Path(), - Path(1), - Path(1, 0), - Path(1, 0, 1), - }); + ValidateSelection(selectionModel, path); Select(selectionModel, Path(0, 0, 1, 0), true); - ValidateSelection(selectionModel, - new List() - { - Path(0, 0, 1, 0) - }, - new List() - { - Path(), - Path(0), - Path(0, 0), - Path(0, 0, 1) - }); + ValidateSelection(selectionModel, Path(0, 0, 1, 0)); Select(selectionModel, Path(0, 0, 1, 0), false); - ValidateSelection(selectionModel, new List() { }); + ValidateSelection(selectionModel); } [Theory] @@ -263,15 +199,7 @@ namespace Avalonia.Controls.UnitTests var startPath = Path(1, 0, 1, 0); Select(selectionModel, startPath, true); - ValidateSelection(selectionModel, - new List() { startPath }, - new List() - { - Path(), - Path(1), - Path(1, 0), - Path(1, 0, 1) - }); + ValidateSelection(selectionModel, startPath); var endPath = Path(1, 1, 1, 0); SelectRangeFromAnchor(selectionModel, endPath, true /* select */); @@ -306,72 +234,45 @@ namespace Avalonia.Controls.UnitTests } ValidateSelection(selectionModel, - new List() - { - Path(1, 0), - Path(1, 1), - Path(1, 0, 1), - Path(1, 0, 1, 0), - Path(1, 0, 1, 1), - Path(1, 0, 1, 2), - Path(1, 0, 1, 3), - Path(1, 1, 0), - Path(1, 1, 1), - Path(1, 1, 0, 0), - Path(1, 1, 0, 1), - Path(1, 1, 0, 2), - Path(1, 1, 0, 3), - Path(1, 1, 1, 0), - }, - new List() - { - Path(), - Path(1), - Path(1, 0), - Path(1, 1), - Path(1, 1, 1), - }); + Path(1, 1), + Path(1, 0, 1, 0), + Path(1, 0, 1, 1), + Path(1, 0, 1, 2), + Path(1, 0, 1, 3), + Path(1, 1, 0), + Path(1, 1, 1), + Path(1, 1, 0, 0), + Path(1, 1, 0, 1), + Path(1, 1, 0, 2), + Path(1, 1, 0, 3), + Path(1, 1, 1, 0)); ClearSelection(selectionModel); - ValidateSelection(selectionModel, new List() { }); + ValidateSelection(selectionModel); startPath = Path(0, 1, 0, 2); SetAnchorIndex(selectionModel, startPath); endPath = Path(0, 0, 0, 2); SelectRangeFromAnchor(selectionModel, endPath, true /* select */); ValidateSelection(selectionModel, - new List() - { - Path(0, 0), - Path(0, 1), - Path(0, 0, 0), - Path(0, 0, 1), - Path(0, 0, 0, 2), - Path(0, 0, 0, 3), - Path(0, 0, 1, 0), - Path(0, 0, 1, 1), - Path(0, 0, 1, 2), - Path(0, 0, 1, 3), - Path(0, 1, 0), - Path(0, 1, 0, 0), - Path(0, 1, 0, 1), - Path(0, 1, 0, 2), - }, - new List() - { - Path(), - Path(0), - Path(0, 0), - Path(0, 0, 0), - Path(0, 1), - Path(0, 1, 0), - }); + Path(0, 1), + Path(0, 0, 1), + Path(0, 0, 0, 2), + Path(0, 0, 0, 3), + Path(0, 0, 1, 0), + Path(0, 0, 1, 1), + Path(0, 0, 1, 2), + Path(0, 0, 1, 3), + Path(0, 1, 0), + Path(0, 1, 0, 0), + Path(0, 1, 0, 1), + Path(0, 1, 0, 2)); startPath = Path(0, 1, 0, 2); SetAnchorIndex(selectionModel, startPath); endPath = Path(0, 0, 0, 2); SelectRangeFromAnchor(selectionModel, endPath, false /* select */); - ValidateSelection(selectionModel, new List() { }); + ValidateSelection(selectionModel); } [Fact] @@ -385,64 +286,36 @@ namespace Avalonia.Controls.UnitTests selectionModel.Select(4); selectionModel.Select(5); ValidateSelection(selectionModel, - new List() - { - Path(3), - Path(4), - Path(5), - }, - new List() - { - Path() - }); + Path(3), + Path(4), + Path(5)); _output.WriteLine("Insert in selected range: Inserting 3 items at index 4"); data.Insert(4, 41); data.Insert(4, 42); data.Insert(4, 43); ValidateSelection(selectionModel, - new List() - { - Path(3), - Path(7), - Path(8), - }, - new List() - { - Path() - }); + Path(3), + Path(7), + Path(8)); _output.WriteLine("Insert before selected range: Inserting 3 items at index 0"); data.Insert(0, 100); data.Insert(0, 101); data.Insert(0, 102); ValidateSelection(selectionModel, - new List() - { - Path(6), - Path(10), - Path(11), - }, - new List() - { - Path() - }); + Path(6), + Path(10), + Path(11)); _output.WriteLine("Insert after selected range: Inserting 3 items at index 12"); data.Insert(12, 1000); data.Insert(12, 1001); data.Insert(12, 1002); ValidateSelection(selectionModel, - new List() - { - Path(6), - Path(10), - Path(11), - }, - new List() - { - Path() - }); + Path(6), + Path(10), + Path(11)); } [Fact] @@ -453,42 +326,15 @@ namespace Avalonia.Controls.UnitTests selectionModel.Source = data; selectionModel.Select(1, 1); - ValidateSelection(selectionModel, - new List() - { - Path(1, 1), - }, - new List() - { - Path(), - Path(1), - }); + ValidateSelection(selectionModel, Path(1, 1)); _output.WriteLine("Insert before selected range: Inserting item at group index 0"); data.Insert(0, 100); - ValidateSelection(selectionModel, - new List() - { - Path(2, 1) - }, - new List() - { - Path(), - Path(2), - }); + ValidateSelection(selectionModel, Path(2, 1)); _output.WriteLine("Insert after selected range: Inserting item at group index 3"); data.Insert(3, 1000); - ValidateSelection(selectionModel, - new List() - { - Path(2, 1) - }, - new List() - { - Path(), - Path(2), - }); + ValidateSelection(selectionModel, Path(2, 1)); } [Fact] @@ -502,58 +348,26 @@ namespace Avalonia.Controls.UnitTests selectionModel.Select(7); selectionModel.Select(8); ValidateSelection(selectionModel, - new List() - { - Path(6), - Path(7), - Path(8) - }, - new List() - { - Path() - }); + Path(6), + Path(7), + Path(8)); _output.WriteLine("Remove before selected range: Removing item at index 0"); data.RemoveAt(0); ValidateSelection(selectionModel, - new List() - { - Path(5), - Path(6), - Path(7) - }, - new List() - { - Path() - }); + Path(5), + Path(6), + Path(7)); _output.WriteLine("Remove from before to middle of selected range: Removing items at index 3, 4, 5"); data.RemoveAt(3); data.RemoveAt(3); data.RemoveAt(3); - ValidateSelection(selectionModel, - new List() - { - Path(3), - Path(4) - }, - new List() - { - Path() - }); + ValidateSelection(selectionModel, Path(3), Path(4)); _output.WriteLine("Remove after selected range: Removing item at index 5"); data.RemoveAt(5); - ValidateSelection(selectionModel, - new List() - { - Path(3), - Path(4) - }, - new List() - { - Path() - }); + ValidateSelection(selectionModel, Path(3), Path(4)); } [Fact] @@ -565,49 +379,19 @@ namespace Avalonia.Controls.UnitTests selectionModel.Select(1, 1); selectionModel.Select(1, 2); - ValidateSelection(selectionModel, - new List() - { - Path(1, 1), - Path(1, 2) - }, - new List() - { - Path(), - Path(1), - }); + ValidateSelection(selectionModel, Path(1, 1), Path(1, 2)); _output.WriteLine("Remove before selected range: Removing item at group index 0"); data.RemoveAt(0); - ValidateSelection(selectionModel, - new List() - { - Path(0, 1), - Path(0, 2) - }, - new List() - { - Path(), - Path(0), - }); + ValidateSelection(selectionModel, Path(0, 1), Path(0, 2)); _output.WriteLine("Remove after selected range: Removing item at group index 1"); data.RemoveAt(1); - ValidateSelection(selectionModel, - new List() - { - Path(0, 1), - Path(0, 2) - }, - new List() - { - Path(), - Path(0), - }); + ValidateSelection(selectionModel, Path(0, 1), Path(0, 2)); _output.WriteLine("Remove group containing selected items"); data.RemoveAt(0); - ValidateSelection(selectionModel, new List()); + ValidateSelection(selectionModel); } [Fact] @@ -620,29 +404,11 @@ namespace Avalonia.Controls.UnitTests selectionModel.Select(3); selectionModel.Select(4); selectionModel.Select(5); - ValidateSelection(selectionModel, - new List() - { - Path(3), - Path(4), - Path(5), - }, - new List() - { - Path() - }); + ValidateSelection(selectionModel, Path(3), Path(4), Path(5)); data[3] = 300; data[4] = 400; - ValidateSelection(selectionModel, - new List() - { - Path(5), - }, - new List() - { - Path() - }); + ValidateSelection(selectionModel, Path(5)); } [Fact] @@ -653,19 +419,10 @@ namespace Avalonia.Controls.UnitTests selectionModel.Source = data; selectionModel.Select(1, 1); - ValidateSelection(selectionModel, - new List() - { - Path(1, 1) - }, - new List() - { - Path(), - Path(1) - }); + ValidateSelection(selectionModel, Path(1, 1)); data[1] = new ObservableCollection(Enumerable.Range(0, 5)); - ValidateSelection(selectionModel, new List()); + ValidateSelection(selectionModel); } [Fact] @@ -678,20 +435,10 @@ namespace Avalonia.Controls.UnitTests selectionModel.Select(3); selectionModel.Select(4); selectionModel.Select(5); - ValidateSelection(selectionModel, - new List() - { - Path(3), - Path(4), - Path(5), - }, - new List() - { - Path() - }); + ValidateSelection(selectionModel, Path(3), Path(4), Path(5)); data.Clear(); - ValidateSelection(selectionModel, new List()); + ValidateSelection(selectionModel); } [Fact] @@ -702,19 +449,10 @@ namespace Avalonia.Controls.UnitTests selectionModel.Source = data; selectionModel.Select(1, 1); - ValidateSelection(selectionModel, - new List() - { - Path(1, 1) - }, - new List() - { - Path(), - Path(1) - }); + ValidateSelection(selectionModel, Path(1, 1)); (data[1] as IList).Clear(); - ValidateSelection(selectionModel, new List()); + ValidateSelection(selectionModel); } // In some cases the leaf node might get a collection change that affects an ancestors selection @@ -734,54 +472,22 @@ namespace Avalonia.Controls.UnitTests selectionModel.Select(1, 0); selectionModel.Select(1, 1); selectionModel.Select(1, 2); - ValidateSelection(selectionModel, - new List() - { - Path(1, 0), - Path(1, 1), - Path(1, 2), - Path(1) - }, - new List() - { - Path(), - }, - 1 /* selectedInnerNodes */); + ValidateSelection(selectionModel, Path(1, 0), Path(1, 1), Path(1, 2)); _output.WriteLine("Inserting 1.0"); selectionChangedRaised = false; (data[1] as AvaloniaList).Insert(0, 100); Assert.True(selectionChangedRaised, "SelectionChanged event was not raised"); - ValidateSelection(selectionModel, - new List() - { - Path(1, 1), - Path(1, 2), - Path(1, 3), - }, - new List() - { - Path(), - Path(1), - }); + ValidateSelection(selectionModel, Path(1, 1), Path(1, 2), Path(1, 3)); _output.WriteLine("Removing 1.0"); selectionChangedRaised = false; (data[1] as AvaloniaList).RemoveAt(0); Assert.True(selectionChangedRaised, "SelectionChanged event was not raised"); ValidateSelection(selectionModel, - new List() - { - Path(1, 0), - Path(1, 1), - Path(1, 2), - Path(1) - }, - new List() - { - Path(), - }, - 1 /* selectedInnerNodes */); + Path(1, 0), + Path(1, 1), + Path(1, 2)); } [Fact] @@ -859,21 +565,33 @@ namespace Avalonia.Controls.UnitTests selectionModel.SelectRange(IndexPath.CreateFrom(0), IndexPath.CreateFrom(1, 1)); ValidateSelection(selectionModel, - new List() + Path(0), + Path(1), + Path(0, 0), + Path(0, 1), + Path(0, 2), + Path(1, 0), + Path(1, 1)); + } + + [Fact] + public void SelectRange_Should_Select_Nested_Items_On_Different_Levels() + { + var target = new SelectionModel(); + var data = CreateNestedData(1, 2, 3); + + target.Source = data; + target.AnchorIndex = new IndexPath(0, 1); + target.SelectRange(Path(0, 1), Path(1)); + + Assert.Equal( + new[] { - Path(0), Path(1), - Path(0, 0), Path(0, 1), Path(0, 2), - Path(1, 0), - Path(1, 1) }, - new List() - { - Path(), - Path(1) - }); + target.SelectedIndices); } [Fact] @@ -2145,75 +1863,9 @@ namespace Avalonia.Controls.UnitTests private void ValidateSelection( SelectionModel selectionModel, - List expectedSelected, - List expectedPartialSelected = null, - int selectedInnerNodes = 0) + params IndexPath[] expectedSelected) { - _output.WriteLine("Validating Selection..."); - - _output.WriteLine("Selection contains indices:"); - foreach (var index in selectionModel.SelectedIndices) - { - _output.WriteLine(" " + index.ToString()); - } - - _output.WriteLine("Selection contains items:"); - foreach (var item in selectionModel.SelectedItems) - { - _output.WriteLine(" " + item.ToString()); - } - - if (selectionModel.Source != null) - { - List allIndices = GetIndexPathsInSource(selectionModel.Source); - foreach (var index in allIndices) - { - bool? isSelected = selectionModel.IsSelectedWithPartialAt(index); - if (Contains(expectedSelected, index) && !Contains(expectedPartialSelected, index)) - { - Assert.True(isSelected.Value, index + " is Selected"); - } - else if (expectedPartialSelected != null && Contains(expectedPartialSelected, index)) - { - Assert.True(isSelected == null, index + " is partially Selected"); - } - else - { - if (isSelected == null) - { - _output.WriteLine("*************" + index + " is null"); - Assert.True(false, "Expected false but got null");; - } - else - { - Assert.False(isSelected.Value, index + " is not Selected"); - } - } - } - } - else - { - foreach (var index in expectedSelected) - { - Assert.True(selectionModel.IsSelectedWithPartialAt(index), index + " is Selected"); - } - } - if (expectedSelected.Count > 0) - { - _output.WriteLine("SelectedIndex is " + selectionModel.SelectedIndex); - Assert.Equal(expectedSelected[0], selectionModel.SelectedIndex); - if (selectionModel.Source != null) - { - Assert.Equal(selectionModel.SelectedItem, GetData(selectionModel, expectedSelected[0])); - } - - int itemsCount = selectionModel.SelectedItems.Count(); - Assert.Equal(selectionModel.Source != null ? expectedSelected.Count - selectedInnerNodes : 0, itemsCount); - int indicesCount = selectionModel.SelectedIndices.Count(); - Assert.Equal(expectedSelected.Count - selectedInnerNodes, indicesCount); - } - - _output.WriteLine("Validating Selection... done"); + Assert.Equal(expectedSelected, selectionModel.SelectedIndices); } private object GetData(SelectionModel selectionModel, IndexPath indexPath)