diff --git a/osu.Framework.Tests/Visual/UserInterface/TestSceneRearrangeableListContainer.cs b/osu.Framework.Tests/Visual/UserInterface/TestSceneRearrangeableListContainer.cs index 83450a664e..9c3ede1100 100644 --- a/osu.Framework.Tests/Visual/UserInterface/TestSceneRearrangeableListContainer.cs +++ b/osu.Framework.Tests/Visual/UserInterface/TestSceneRearrangeableListContainer.cs @@ -1,8 +1,6 @@ // Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. // See the LICENCE file in the repository root for full licence text. -#nullable disable - using System; using System.Collections.Generic; using System.Linq; @@ -21,9 +19,8 @@ namespace osu.Framework.Tests.Visual.UserInterface { public partial class TestSceneRearrangeableListContainer : ManualInputManagerTestScene { - private TestRearrangeableList list; - - private Container listContainer; + private TestRearrangeableList list = null!; + private Container listContainer = null!; [SetUp] public void Setup() => Schedule(() => @@ -85,7 +82,7 @@ public void TestRemoveItem() addItems(item_count); - List items = null; + List items = null!; AddStep("get item references", () => items = new List(list.ItemMap.Values.ToList())); @@ -278,7 +275,7 @@ public void TestNotScrolledToTopOnRemove() [Test] public void TestRemoveDuringLoadAndReAdd() { - TestDelayedLoadRearrangeableList delayedList = null; + TestDelayedLoadRearrangeableList delayedList = null!; AddStep("create list", () => Child = delayedList = new TestDelayedLoadRearrangeableList()); @@ -327,6 +324,24 @@ public void TestDragSynchronisation() }); } + [Test] + public void TestReplaceEntireList() + { + addItems(1); + + AddStep("replace list", () => list.Items.ReplaceRange(0, list.Items.Count, [100])); + AddUntilStep("wait for items to load", () => list.ItemMap.Values.All(i => i.IsLoaded)); + } + + [Test] + public void TestPartialReplace() + { + addItems(5); + + AddStep("replace list", () => list.Items.ReplaceRange(2, 2, [100, 101])); + AddUntilStep("wait for items to load", () => list.ItemMap.Values.All(i => i.IsLoaded)); + } + private void addDragSteps(int from, int to, int[] expectedSequence) { AddStep($"move to {from}", () => diff --git a/osu.Framework/Graphics/Containers/RearrangeableListContainer.cs b/osu.Framework/Graphics/Containers/RearrangeableListContainer.cs index 1363206638..b133534c31 100644 --- a/osu.Framework/Graphics/Containers/RearrangeableListContainer.cs +++ b/osu.Framework/Graphics/Containers/RearrangeableListContainer.cs @@ -183,11 +183,15 @@ private void sortItems() { for (int i = 0; i < Items.Count; i++) { - var drawable = itemMap[Items[i]]; + // A drawable for the item may not exist yet, for example in a replace-range operation where the removal happens first. + if (!itemMap.TryGetValue(Items[i], out var drawable)) + continue; + + // The item may not be loaded yet, because add operations are asynchronous. + if (drawable.Parent != ListContainer) + continue; - // If the async load didn't complete, the item wouldn't exist in the container and an exception would be thrown - if (drawable.Parent == ListContainer) - ListContainer!.SetLayoutPosition(drawable, i); + ListContainer!.SetLayoutPosition(drawable, i); } }