diff --git a/src/Core/src/Handlers/Layout/LayoutExtensions.cs b/src/Core/src/Handlers/Layout/LayoutExtensions.cs index 500a311708e5..4bcde8f28730 100644 --- a/src/Core/src/Handlers/Layout/LayoutExtensions.cs +++ b/src/Core/src/Handlers/Layout/LayoutExtensions.cs @@ -5,16 +5,25 @@ namespace Microsoft.Maui.Handlers { internal static class LayoutExtensions { - class ZIndexComparer : IComparer + record ViewAndIndex(IView View, int Index); + + class ZIndexComparer : IComparer { - public int Compare(IView? x, IView? y) + public int Compare(ViewAndIndex? x, ViewAndIndex? y) { - if (x == null || y == null) + if(x == null || y == null) { return 0; } - return x.ZIndex.CompareTo(y.ZIndex); + var zIndexCompare = x.View.ZIndex.CompareTo(y.View.ZIndex); + + if (zIndexCompare == 0) + { + return x.Index.CompareTo(y.Index); + } + + return zIndexCompare; } } @@ -22,9 +31,23 @@ public int Compare(IView? x, IView? y) public static IView[] OrderByZIndex(this ILayout layout) { - var ordered = new IView[layout.Count]; - layout.CopyTo(ordered, 0); - Array.Sort(ordered, s_comparer); + var count = layout.Count; + var indexedViews = new ViewAndIndex[count]; + + for (int n = 0; n < count; n++) + { + indexedViews[n] = new ViewAndIndex(layout[n], n); + } + + Array.Sort(indexedViews, s_comparer); + + var ordered = new IView[count]; + + for (int n = 0; n < count; n++) + { + ordered[n] = indexedViews[n].View; + } + return ordered; } diff --git a/src/Core/tests/UnitTests/Layouts/ZIndexTests.cs b/src/Core/tests/UnitTests/Layouts/ZIndexTests.cs index 6bde71bec931..5127df109de6 100644 --- a/src/Core/tests/UnitTests/Layouts/ZIndexTests.cs +++ b/src/Core/tests/UnitTests/Layouts/ZIndexTests.cs @@ -243,5 +243,37 @@ public void ZIndexUpdatePreservesAddOrderForEqualZIndexes() Assert.Equal(view2, zordered[2]); Assert.Equal(view3, zordered[3]); } + + [Fact] + public void ZIndexUpdatePreservesAddOrderLotsOfEqualZIndexes() + { + // This tests the same thing as ZIndexUpdatePreservesAddOrderForEqualZIndexes, + // but for more views - since the sorting algorithm can change when the arrays + // are larger, we were running into situations where layouts with more controls + // were _not_ preserving the Add() order when sorting by z-index. + + var layout = new FakeLayout(); + + int views = 100; + int iterations = 10; + + var toAdd = new IView[views]; + + for (int n = 0; n < views; n++) + { + toAdd[n] = CreateTestView(zIndex: 0); + layout.Add(toAdd[n]); + } + + for (int i = 0; i < iterations; i++) + { + var zordered = layout.OrderByZIndex(); + + for (int n = 0; n < zordered.Length; n++) + { + Assert.Equal(toAdd[n], zordered[n]); + } + } + } } }