Browse Source

Remove ItemsRepeater from the main repostiory (#14989)

See https://github.com/AvaloniaUI/Avalonia.Controls.ItemsRepeater
Max Katz 1 year ago
parent
commit
5537e97aa5
54 changed files with 1 additions and 7798 deletions
  1. 0 2
      Avalonia.Desktop.slnf
  2. 0 13
      Avalonia.sln
  3. 0 1
      samples/ControlCatalog/ControlCatalog.csproj
  4. 0 5
      samples/ControlCatalog/MainView.xaml
  5. 0 74
      samples/ControlCatalog/Pages/ItemsRepeaterPage.xaml
  6. 0 157
      samples/ControlCatalog/Pages/ItemsRepeaterPage.xaml.cs
  7. 0 91
      samples/ControlCatalog/ViewModels/ItemsRepeaterPageViewModel.cs
  8. 0 18
      src/Avalonia.Controls.ItemsRepeater/Avalonia.Controls.ItemsRepeater.csproj
  9. 0 27
      src/Avalonia.Controls.ItemsRepeater/Controls/ElementFactory.cs
  10. 0 66
      src/Avalonia.Controls.ItemsRepeater/Controls/IElementFactory.cs
  11. 0 67
      src/Avalonia.Controls.ItemsRepeater/Controls/ItemTemplateWrapper.cs
  12. 0 792
      src/Avalonia.Controls.ItemsRepeater/Controls/ItemsRepeater.cs
  13. 0 24
      src/Avalonia.Controls.ItemsRepeater/Controls/ItemsRepeaterElementClearingEventArgs.cs
  14. 0 44
      src/Avalonia.Controls.ItemsRepeater/Controls/ItemsRepeaterElementIndexChangedEventArgs.cs
  15. 0 35
      src/Avalonia.Controls.ItemsRepeater/Controls/ItemsRepeaterElementPreparedEventArgs.cs
  16. 0 112
      src/Avalonia.Controls.ItemsRepeater/Controls/RecyclePool.cs
  17. 0 117
      src/Avalonia.Controls.ItemsRepeater/Controls/RecyclingElementFactory.cs
  18. 0 70
      src/Avalonia.Controls.ItemsRepeater/Controls/RepeaterLayoutContext.cs
  19. 0 54
      src/Avalonia.Controls.ItemsRepeater/Controls/UniqueIdElementPool.cs
  20. 0 782
      src/Avalonia.Controls.ItemsRepeater/Controls/ViewManager.cs
  21. 0 550
      src/Avalonia.Controls.ItemsRepeater/Controls/ViewportManager.cs
  22. 0 119
      src/Avalonia.Controls.ItemsRepeater/Controls/VirtualizationInfo.cs
  23. 0 219
      src/Avalonia.Controls.ItemsRepeater/Layout/AttachedLayout.cs
  24. 0 463
      src/Avalonia.Controls.ItemsRepeater/Layout/ElementManager.cs
  25. 0 767
      src/Avalonia.Controls.ItemsRepeater/Layout/FlowLayoutAlgorithm.cs
  26. 0 44
      src/Avalonia.Controls.ItemsRepeater/Layout/IFlowLayoutAlgorithmDelegates.cs
  27. 0 28
      src/Avalonia.Controls.ItemsRepeater/Layout/LayoutContext.cs
  28. 0 45
      src/Avalonia.Controls.ItemsRepeater/Layout/LayoutContextAdapter.cs
  29. 0 83
      src/Avalonia.Controls.ItemsRepeater/Layout/NonVirtualizingLayout.cs
  30. 0 31
      src/Avalonia.Controls.ItemsRepeater/Layout/NonVirtualizingLayoutContext.cs
  31. 0 162
      src/Avalonia.Controls.ItemsRepeater/Layout/NonVirtualizingStackLayout.cs
  32. 0 96
      src/Avalonia.Controls.ItemsRepeater/Layout/OrientationBasedMeasures.cs
  33. 0 365
      src/Avalonia.Controls.ItemsRepeater/Layout/StackLayout.cs
  34. 0 61
      src/Avalonia.Controls.ItemsRepeater/Layout/StackLayoutState.cs
  35. 0 562
      src/Avalonia.Controls.ItemsRepeater/Layout/UniformGridLayout.cs
  36. 0 204
      src/Avalonia.Controls.ItemsRepeater/Layout/UniformGridLayoutState.cs
  37. 0 27
      src/Avalonia.Controls.ItemsRepeater/Layout/Utils/ListUtils.cs
  38. 0 37
      src/Avalonia.Controls.ItemsRepeater/Layout/UvBounds.cs
  39. 0 45
      src/Avalonia.Controls.ItemsRepeater/Layout/UvMeasure.cs
  40. 0 42
      src/Avalonia.Controls.ItemsRepeater/Layout/VirtualLayoutContextAdapter.cs
  41. 0 119
      src/Avalonia.Controls.ItemsRepeater/Layout/VirtualizingLayout.cs
  42. 0 195
      src/Avalonia.Controls.ItemsRepeater/Layout/VirtualizingLayoutContext.cs
  43. 0 25
      src/Avalonia.Controls.ItemsRepeater/Layout/WrapItem.cs
  44. 0 336
      src/Avalonia.Controls.ItemsRepeater/Layout/WrapLayout.cs
  45. 0 146
      src/Avalonia.Controls.ItemsRepeater/Layout/WrapLayoutState.cs
  46. 0 4
      src/Avalonia.Controls.ItemsRepeater/Properties/AssemblyInfo.cs
  47. 0 22
      tests/Avalonia.Controls.ItemsRepeater.UnitTests/Avalonia.Controls.ItemsRepeater.UnitTests.csproj
  48. 0 24
      tests/Avalonia.Controls.ItemsRepeater.UnitTests/ItemsRepeaterTests.cs
  49. 0 336
      tests/Avalonia.Controls.ItemsRepeater.UnitTests/NonVirtualizingStackLayoutTests.cs
  50. 0 1
      tests/Avalonia.LeakTests/Avalonia.LeakTests.csproj
  51. 1 35
      tests/Avalonia.LeakTests/ControlTests.cs
  52. 0 1
      tests/Avalonia.Markup.Xaml.UnitTests/Avalonia.Markup.Xaml.UnitTests.csproj
  53. 0 52
      tests/Avalonia.Markup.Xaml.UnitTests/Xaml/StyleTests.cs
  54. 0 1
      tests/Avalonia.Markup.Xaml.UnitTests/XamlTestBase.cs

+ 0 - 2
Avalonia.Desktop.slnf

@@ -17,7 +17,6 @@
       "src\\Avalonia.Build.Tasks\\Avalonia.Build.Tasks.csproj",
       "src\\Avalonia.Controls.ColorPicker\\Avalonia.Controls.ColorPicker.csproj",
       "src\\Avalonia.Controls.DataGrid\\Avalonia.Controls.DataGrid.csproj",
-      "src\\Avalonia.Controls.ItemsRepeater\\Avalonia.Controls.ItemsRepeater.csproj",
       "src\\Avalonia.Controls\\Avalonia.Controls.csproj",
       "src\\Avalonia.DesignerSupport\\Avalonia.DesignerSupport.csproj",
       "src\\Avalonia.Desktop\\Avalonia.Desktop.csproj",
@@ -52,7 +51,6 @@
       "tests\\Avalonia.Benchmarks\\Avalonia.Benchmarks.csproj",
       "tests\\Avalonia.Build.Tasks.UnitTest\\Avalonia.Build.Tasks.UnitTest.csproj",
       "tests\\Avalonia.Controls.DataGrid.UnitTests\\Avalonia.Controls.DataGrid.UnitTests.csproj",
-      "tests\\Avalonia.Controls.ItemsRepeater.UnitTests\\Avalonia.Controls.ItemsRepeater.UnitTests.csproj",
       "tests\\Avalonia.Controls.UnitTests\\Avalonia.Controls.UnitTests.csproj",
       "tests\\Avalonia.DesignerSupport.TestApp\\Avalonia.DesignerSupport.TestApp.csproj",
       "tests\\Avalonia.DesignerSupport.Tests\\Avalonia.DesignerSupport.Tests.csproj",

+ 0 - 13
Avalonia.sln

@@ -252,10 +252,6 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution
 		Settings.StyleCop = Settings.StyleCop
 	EndProjectSection
 EndProject
-Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Avalonia.Controls.ItemsRepeater", "src\Avalonia.Controls.ItemsRepeater\Avalonia.Controls.ItemsRepeater.csproj", "{EE0F0DD4-A70D-472B-BD5D-B7D32D0E9386}"
-EndProject
-Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Avalonia.Controls.ItemsRepeater.UnitTests", "tests\Avalonia.Controls.ItemsRepeater.UnitTests\Avalonia.Controls.ItemsRepeater.UnitTests.csproj", "{F4E36AA8-814E-4704-BC07-291F70F45193}"
-EndProject
 Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Avalonia.Generators", "src\tools\Avalonia.Generators\Avalonia.Generators.csproj", "{DDA28789-C21A-4654-86CE-D01E81F095C5}"
 EndProject
 Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Avalonia.Fonts.Inter", "src\Avalonia.Fonts.Inter\Avalonia.Fonts.Inter.csproj", "{13F1135D-BA1A-435C-9C5B-A368D1D63DE4}"
@@ -621,14 +617,6 @@ Global
 		{C692FE73-43DB-49CE-87FC-F03ED61F25C9}.Debug|Any CPU.Build.0 = Release|Any CPU
 		{C692FE73-43DB-49CE-87FC-F03ED61F25C9}.Release|Any CPU.ActiveCfg = Release|Any CPU
 		{C692FE73-43DB-49CE-87FC-F03ED61F25C9}.Release|Any CPU.Build.0 = Release|Any CPU
-		{EE0F0DD4-A70D-472B-BD5D-B7D32D0E9386}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
-		{EE0F0DD4-A70D-472B-BD5D-B7D32D0E9386}.Debug|Any CPU.Build.0 = Debug|Any CPU
-		{EE0F0DD4-A70D-472B-BD5D-B7D32D0E9386}.Release|Any CPU.ActiveCfg = Release|Any CPU
-		{EE0F0DD4-A70D-472B-BD5D-B7D32D0E9386}.Release|Any CPU.Build.0 = Release|Any CPU
-		{F4E36AA8-814E-4704-BC07-291F70F45193}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
-		{F4E36AA8-814E-4704-BC07-291F70F45193}.Debug|Any CPU.Build.0 = Debug|Any CPU
-		{F4E36AA8-814E-4704-BC07-291F70F45193}.Release|Any CPU.ActiveCfg = Release|Any CPU
-		{F4E36AA8-814E-4704-BC07-291F70F45193}.Release|Any CPU.Build.0 = Release|Any CPU
 		{DDA28789-C21A-4654-86CE-D01E81F095C5}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
 		{DDA28789-C21A-4654-86CE-D01E81F095C5}.Debug|Any CPU.Build.0 = Debug|Any CPU
 		{DDA28789-C21A-4654-86CE-D01E81F095C5}.Release|Any CPU.ActiveCfg = Release|Any CPU
@@ -779,7 +767,6 @@ Global
 		{75C47156-C5D8-44BC-A5A7-E8657C2248D6} = {9B9E3891-2366-4253-A952-D08BCEB71098}
 		{C810060E-3809-4B74-A125-F11533AF9C1B} = {9B9E3891-2366-4253-A952-D08BCEB71098}
 		{C692FE73-43DB-49CE-87FC-F03ED61F25C9} = {4ED8B739-6F4E-4CD4-B993-545E6B5CE637}
-		{F4E36AA8-814E-4704-BC07-291F70F45193} = {C5A00AC3-B34C-4564-9BDD-2DA473EF4D8B}
 		{DDA28789-C21A-4654-86CE-D01E81F095C5} = {4ED8B739-6F4E-4CD4-B993-545E6B5CE637}
 		{A82AD1BC-EBE6-4FC3-A13B-D52A50297533} = {9B9E3891-2366-4253-A952-D08BCEB71098}
 		{F8928267-688E-4A51-989C-612A72446D33} = {9B9E3891-2366-4253-A952-D08BCEB71098}

+ 0 - 1
samples/ControlCatalog/ControlCatalog.csproj

@@ -27,7 +27,6 @@
     <ProjectReference Include="..\..\packages\Avalonia\Avalonia.csproj" />
     <ProjectReference Include="..\..\src\Avalonia.Controls.ColorPicker\Avalonia.Controls.ColorPicker.csproj" />
     <ProjectReference Include="..\..\src\Avalonia.Controls.DataGrid\Avalonia.Controls.DataGrid.csproj" />
-    <ProjectReference Include="..\..\src\Avalonia.Controls.ItemsRepeater\Avalonia.Controls.ItemsRepeater.csproj" />
     <ProjectReference Include="..\..\src\Avalonia.Themes.Simple\Avalonia.Themes.Simple.csproj" />
     <ProjectReference Include="..\..\src\Avalonia.Themes.Fluent\Avalonia.Themes.Fluent.csproj" />
     <ProjectReference Include="..\..\src\Avalonia.Fonts.Inter\Avalonia.Fonts.Inter.csproj" />

+ 0 - 5
samples/ControlCatalog/MainView.xaml

@@ -106,11 +106,6 @@
                ScrollViewer.VerticalScrollBarVisibility="Disabled">
         <pages:ImagePage />
       </TabItem>
-      <TabItem Header="ItemsRepeater"
-               ScrollViewer.HorizontalScrollBarVisibility="Disabled"
-               ScrollViewer.VerticalScrollBarVisibility="Disabled">
-        <pages:ItemsRepeaterPage />
-      </TabItem>
       <TabItem Header="Label">
         <pages:LabelsPage />
       </TabItem>

+ 0 - 74
samples/ControlCatalog/Pages/ItemsRepeaterPage.xaml

@@ -1,74 +0,0 @@
-<UserControl xmlns="https://github.com/avaloniaui"
-             xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
-             xmlns:viewModels="using:ControlCatalog.ViewModels"
-             x:Class="ControlCatalog.Pages.ItemsRepeaterPage"
-             x:DataType="viewModels:ItemsRepeaterPageViewModel">
-  <UserControl.Styles>
-    <Style Selector="ItemsRepeater TextBlock.oddTemplate">
-      <Setter Property="Background" Value="Yellow" />
-      <Setter Property="Foreground" Value="Black" />
-    </Style>
-    <Style Selector="ItemsRepeater TextBlock.evenTemplate">
-      <Setter Property="Background" Value="Wheat" />
-      <Setter Property="Foreground" Value="Black" />
-    </Style>
-    <Style Selector="ItemsRepeater TextBlock:nth-child(5n+3)">
-      <Setter Property="Foreground" Value="Red" />
-      <Setter Property="FontWeight" Value="Bold" />
-    </Style>
-    <Style Selector="ItemsRepeater TextBlock:nth-last-child(5n+4)">
-      <Setter Property="Foreground" Value="Blue" />
-      <Setter Property="FontWeight" Value="Bold" />
-    </Style>
-  </UserControl.Styles>
-  <UserControl.Resources>
-    <RecyclePool x:Key="RecyclePool" />
-    <DataTemplate x:Key="odd" x:DataType="viewModels:ItemsRepeaterPageViewModelItem">
-      <TextBlock Classes="oddTemplate"
-                 Height="{Binding Height}"
-                 Text="{Binding Text}"/>
-    </DataTemplate>
-    <DataTemplate x:Key="even" x:DataType="viewModels:ItemsRepeaterPageViewModelItem">
-      <TextBlock Classes="evenTemplate"
-                 Height="{Binding Height}"
-                 Text="{Binding Text}"/>
-    </DataTemplate>
-    <RecyclingElementFactory x:Key="elementFactory"
-                             RecyclePool="{StaticResource RecyclePool}"
-                             SelectTemplateKey="OnSelectTemplateKey">
-      <RecyclingElementFactory.Templates>
-        <StaticResource x:Key="odd" ResourceKey="odd" />
-        <StaticResource x:Key="even" ResourceKey="even" />
-      </RecyclingElementFactory.Templates>
-    </RecyclingElementFactory>
-  </UserControl.Resources>
-  
-  <DockPanel>
-    <StackPanel DockPanel.Dock="Top" Spacing="4" Margin="0 0 0 16">
-      <TextBlock Classes="h2">A data-driven collection control that incorporates a flexible layout system, custom views, and virtualization.</TextBlock>
-    </StackPanel>
-    <StackPanel DockPanel.Dock="Right" Margin="8 0" Spacing="4">
-      <ComboBox SelectedIndex="0" SelectionChanged="LayoutChanged">
-        <ComboBoxItem>Stack - Vertical</ComboBoxItem>
-        <ComboBoxItem>Stack - Horizontal</ComboBoxItem>
-        <ComboBoxItem>UniformGrid - Vertical</ComboBoxItem>
-        <ComboBoxItem>UniformGrid - Horizontal</ComboBoxItem>
-      </ComboBox>
-      <Button Command="{Binding AddItem}">Add Item</Button>
-      <Button Command="{Binding RemoveItem}">Remove Item</Button>
-      <Button Command="{Binding RandomizeHeights}">Randomize Heights</Button>
-      <Button Command="{Binding ResetItems}">Reset items</Button>
-      <Button x:Name="scrollToLast">Scroll to Last</Button>
-      <Button x:Name="scrollToRandom">Scroll to Random</Button>
-      <Button x:Name="scrollToSelected">Scroll to Selected</Button>
-    </StackPanel>
-    <Border BorderThickness="1" BorderBrush="{DynamicResource CatalogBaseMediumColor}" Margin="0 0 0 16">
-      <ScrollViewer Name="scroller"
-                    HorizontalScrollBarVisibility="Auto"
-                    VerticalScrollBarVisibility="Auto">
-        <ItemsRepeater Name="repeater" Background="Transparent" ItemsSource="{Binding Items}"
-                       ItemTemplate="{StaticResource elementFactory}"/>
-      </ScrollViewer>
-    </Border>
-  </DockPanel>
-</UserControl>

+ 0 - 157
samples/ControlCatalog/Pages/ItemsRepeaterPage.xaml.cs

@@ -1,157 +0,0 @@
-using System;
-using System.Linq;
-using Avalonia.Controls;
-using Avalonia.Controls.Primitives;
-using Avalonia.Input;
-using Avalonia.Layout;
-using Avalonia.Markup.Xaml;
-using Avalonia.VisualTree;
-using ControlCatalog.ViewModels;
-
-namespace ControlCatalog.Pages
-{
-    public class ItemsRepeaterPage : UserControl
-    {
-        private readonly ItemsRepeaterPageViewModel _viewModel;
-        private ItemsRepeater _repeater;
-        private ScrollViewer _scroller;
-        private int _selectedIndex;
-        private Button _scrollToLast;
-        private Button _scrollToRandom;
-        private Button _scrollToSelected;
-        private Random _random = new Random(0);
-
-        public ItemsRepeaterPage()
-        {
-            this.InitializeComponent();
-            _repeater = this.Get<ItemsRepeater>("repeater");
-            _scroller = this.Get<ScrollViewer>("scroller");
-            _scrollToLast = this.Get<Button>("scrollToLast");
-            _scrollToRandom = this.Get<Button>("scrollToRandom");
-            _scrollToSelected = this.Get<Button>("scrollToSelected");
-            _repeater.PointerPressed += RepeaterClick;
-            _repeater.KeyDown += RepeaterOnKeyDown;
-            _scrollToLast.Click += scrollToLast_Click;
-            _scrollToRandom.Click += scrollToRandom_Click;
-            _scrollToSelected.Click += scrollToSelected_Click;
-            DataContext = _viewModel = new ItemsRepeaterPageViewModel();
-        }
-
-        private void InitializeComponent()
-        {
-            AvaloniaXamlLoader.Load(this);
-        }
-
-        public void OnSelectTemplateKey(object sender, SelectTemplateEventArgs e)
-        {
-            if (e.DataContext is ItemsRepeaterPageViewModelItem item)
-            {
-                e.TemplateKey = (item.Index % 2 == 0) ? "even" : "odd";
-            }
-        }
-
-        private void LayoutChanged(object sender, SelectionChangedEventArgs e)
-        {
-            if (_repeater == null)
-            {
-                return;
-            }
-
-            var comboBox = (ComboBox)sender;
-
-            switch (comboBox.SelectedIndex)
-            {
-                case 0:
-                    _scroller.HorizontalScrollBarVisibility = ScrollBarVisibility.Auto;
-                    _scroller.VerticalScrollBarVisibility = ScrollBarVisibility.Auto;
-                    _repeater.Layout = new StackLayout { Orientation = Orientation.Vertical };
-                    break;
-                case 1:
-                    _scroller.HorizontalScrollBarVisibility = ScrollBarVisibility.Auto;
-                    _scroller.VerticalScrollBarVisibility = ScrollBarVisibility.Auto;
-                    _repeater.Layout = new StackLayout { Orientation = Orientation.Horizontal };
-                    break;
-                case 2:
-                    _scroller.HorizontalScrollBarVisibility = ScrollBarVisibility.Auto;
-                    _scroller.VerticalScrollBarVisibility = ScrollBarVisibility.Disabled;
-                    _repeater.Layout = new UniformGridLayout
-                    {
-                        Orientation = Orientation.Vertical,
-                        MinItemWidth = 200,
-                        MinItemHeight = 200,
-                    };
-                    break;
-                case 3:
-                    _scroller.HorizontalScrollBarVisibility = ScrollBarVisibility.Disabled;
-                    _scroller.VerticalScrollBarVisibility = ScrollBarVisibility.Auto;
-                    _repeater.Layout = new UniformGridLayout
-                    {
-                        Orientation = Orientation.Horizontal,
-                        MinItemWidth = 200,
-                        MinItemHeight = 200,
-                    };
-                    break;
-                case 4:
-                    _scroller.HorizontalScrollBarVisibility = ScrollBarVisibility.Auto;
-                    _scroller.VerticalScrollBarVisibility = ScrollBarVisibility.Disabled;
-                    _repeater.Layout = new WrapLayout
-                    {
-                        Orientation = Orientation.Vertical,
-                        HorizontalSpacing = 20,
-                        VerticalSpacing = 20
-                    };
-                    break;
-                case 5:
-                    _scroller.HorizontalScrollBarVisibility = ScrollBarVisibility.Disabled;
-                    _scroller.VerticalScrollBarVisibility = ScrollBarVisibility.Auto;
-                    _repeater.Layout = new WrapLayout
-                    {
-                        Orientation = Orientation.Horizontal,
-                        HorizontalSpacing = 20,
-                        VerticalSpacing = 20
-                    };
-                    break;
-            }
-        }
-
-        private void ScrollTo(int index)
-        {
-            System.Diagnostics.Debug.WriteLine("Scroll to " + index);
-            var element = _repeater.GetOrCreateElement(index);
-            ((TopLevel)VisualRoot!).UpdateLayout();
-            element.BringIntoView();
-        }
-
-        private void RepeaterClick(object? sender, PointerPressedEventArgs e)
-        {
-            if ((e.Source as TextBlock)?.DataContext is ItemsRepeaterPageViewModelItem item)
-            {
-                _viewModel.SelectedItem = item;
-                _selectedIndex = _viewModel.Items.IndexOf(item);
-            }
-        }
-
-        private void RepeaterOnKeyDown(object? sender, KeyEventArgs e)
-        {
-            if (e.Key == Key.F5)
-            {
-                _viewModel.ResetItems();
-            }
-        }
-
-        private void scrollToLast_Click(object? sender, Avalonia.Interactivity.RoutedEventArgs e)
-        {
-            ScrollTo(_viewModel.Items.Count - 1);
-        }
-
-        private void scrollToRandom_Click(object? sender, Avalonia.Interactivity.RoutedEventArgs e)
-        {
-            ScrollTo(_random.Next(_viewModel.Items.Count - 1));
-        }
-
-        private void scrollToSelected_Click(object? sender, Avalonia.Interactivity.RoutedEventArgs e)
-        {
-            ScrollTo(_selectedIndex);
-        }
-    }
-}

+ 0 - 91
samples/ControlCatalog/ViewModels/ItemsRepeaterPageViewModel.cs

@@ -1,91 +0,0 @@
-using System;
-using System.Collections.ObjectModel;
-using System.Linq;
-using Avalonia.Media;
-using MiniMvvm;
-
-namespace ControlCatalog.ViewModels
-{
-    public class ItemsRepeaterPageViewModel : ViewModelBase
-    {
-        private int _newItemIndex = 1;
-        private int _newGenerationIndex = 0;
-        private ObservableCollection<ItemsRepeaterPageViewModelItem> _items;
-
-        public ItemsRepeaterPageViewModel()
-        {
-            _items = CreateItems();
-        }
-
-        public ObservableCollection<ItemsRepeaterPageViewModelItem> Items
-        {
-            get => _items;
-            set => this.RaiseAndSetIfChanged(ref _items, value);
-        }
-
-        public ItemsRepeaterPageViewModelItem? SelectedItem { get; set; }
-
-        public void AddItem()
-        {
-            var index = SelectedItem != null ? Items.IndexOf(SelectedItem) : -1;
-            Items.Insert(index + 1, new ItemsRepeaterPageViewModelItem(index + 1, $"New Item {_newItemIndex++}"));
-        }
-
-        public void RemoveItem()
-        {
-            if (SelectedItem is not null)
-            {
-                Items.Remove(SelectedItem);
-                SelectedItem = null;
-            }
-            else if (Items.Count > 0)
-            {
-                Items.RemoveAt(Items.Count - 1);
-            }
-        }
-
-        public void RandomizeHeights()
-        {
-            var random = new Random();
-
-            foreach (var i in Items)
-            {
-                i.Height = random.Next(240) + 10;
-            }
-        }
-
-        public void ResetItems()
-        {
-            Items = CreateItems();
-        }
-
-        private ObservableCollection<ItemsRepeaterPageViewModelItem> CreateItems()
-        {
-            var suffix = _newGenerationIndex == 0 ? string.Empty : $"[{_newGenerationIndex.ToString()}]";
-
-            _newGenerationIndex++;
-
-            return new ObservableCollection<ItemsRepeaterPageViewModelItem>(
-                Enumerable.Range(1, 100000).Select(i => new ItemsRepeaterPageViewModelItem(i, $"Item {i.ToString()} {suffix}")));
-        }
-    }
-    
-    public class ItemsRepeaterPageViewModelItem : ViewModelBase
-    {
-        private double _height = double.NaN;
-
-        public ItemsRepeaterPageViewModelItem(int index, string text)
-        {
-            Index = index;
-            Text = text;
-        }
-        public int Index { get; }
-        public string Text { get; }
-            
-        public double Height 
-        {
-            get => _height;
-            set => this.RaiseAndSetIfChanged(ref _height, value);
-        }
-    }
-}

+ 0 - 18
src/Avalonia.Controls.ItemsRepeater/Avalonia.Controls.ItemsRepeater.csproj

@@ -1,18 +0,0 @@
-<Project Sdk="Microsoft.NET.Sdk">
-  <PropertyGroup>
-    <TargetFrameworks>$(AvsCurrentTargetFramework);$(AvsLegacyTargetFrameworks);netstandard2.0</TargetFrameworks>
-    <RootNamespace>Avalonia</RootNamespace>
-  </PropertyGroup>
-  <ItemGroup>
-    <Compile Include="..\Avalonia.Base\Metadata\NullableAttributes.cs" Link="NullableAttributes.cs" />
-  </ItemGroup>
-  <ItemGroup>
-    <ProjectReference Include="..\Avalonia.Base\Avalonia.Base.csproj" />
-    <ProjectReference Include="..\Avalonia.Controls\Avalonia.Controls.csproj" />
-  </ItemGroup>
-  <Import Project="..\..\build\EmbedXaml.props" />
-  <Import Project="..\..\build\BuildTargets.targets" />
-  <Import Project="..\..\build\NullableEnable.props" />
-  <Import Project="..\..\build\TrimmingEnable.props" />
-  <Import Project="..\..\build\DevAnalyzers.props" />
-</Project>

+ 0 - 27
src/Avalonia.Controls.ItemsRepeater/Controls/ElementFactory.cs

@@ -1,27 +0,0 @@
-using Avalonia.Controls.Templates;
-
-namespace Avalonia.Controls
-{
-    public abstract class ElementFactory : IElementFactory
-    {
-        public Control Build(object? data)
-        {
-            return GetElementCore(new ElementFactoryGetArgs { Data = data });
-        }
-
-        public Control GetElement(ElementFactoryGetArgs args)
-        {
-            return GetElementCore(args);
-        }
-
-        public bool Match(object? data) => true;
-
-        public void RecycleElement(ElementFactoryRecycleArgs args)
-        {
-            RecycleElementCore(args);
-        }
-
-        protected abstract Control GetElementCore(ElementFactoryGetArgs args);
-        protected abstract void RecycleElementCore(ElementFactoryRecycleArgs args);
-    }
-}

+ 0 - 66
src/Avalonia.Controls.ItemsRepeater/Controls/IElementFactory.cs

@@ -1,66 +0,0 @@
-using Avalonia.Controls.Templates;
-
-namespace Avalonia.Controls
-{
-    /// <summary>
-    /// Represents the optional arguments to use when calling an implementation of the
-    /// <see cref="IElementFactory"/>'s <see cref="IElementFactory.GetElement"/> method.
-    /// </summary>
-    public class ElementFactoryGetArgs
-    {
-        /// <summary>
-        /// Gets or sets the data item for which an appropriate element tree should be realized
-        /// when calling <see cref="IElementFactory.GetElement"/>.
-        /// </summary>
-        public object? Data { get; set; }
-
-        /// <summary>
-        /// Gets or sets the <see cref="Control"/> that is expected to be the parent of the
-        /// realized element from <see cref="IElementFactory.GetElement"/>.
-        /// </summary>
-        public Control? Parent { get; set; }
-
-        /// <summary>
-        /// Gets or sets the index of the item that should be realized.
-        /// </summary>
-        public int Index { get; set; }
-    }
-
-    /// <summary>
-    /// Represents the optional arguments to use when calling an implementation of the
-    /// <see cref="IElementFactory"/>'s <see cref="IElementFactory.GetElement"/> method.
-    /// </summary>
-    public class ElementFactoryRecycleArgs
-    {
-        /// <summary>
-        /// Gets or sets the <see cref="Control"/> to recycle when calling 
-        /// <see cref="IElementFactory.RecycleElement"/>.
-        /// </summary>
-        public Control? Element { get; set; }
-
-        /// <summary>
-        /// Gets or sets the <see cref="Control"/> that is expected to be the parent of the
-        /// realized element from <see cref="IElementFactory.GetElement"/>.
-        /// </summary>
-        public Control? Parent { get; set; }
-    }
-
-    /// <summary>
-    /// A data template that supports creating and recycling elements for an <see cref="ItemsRepeater"/>.
-    /// </summary>
-    public interface IElementFactory : IDataTemplate
-    {
-        /// <summary>
-        /// Gets an <see cref="Control"/>.
-        /// </summary>
-        /// <param name="args">The element args.</param>
-        public Control GetElement(ElementFactoryGetArgs args);
-
-        /// <summary>
-        /// Recycles an <see cref="Control"/> that was previously retrieved using
-        /// <see cref="GetElement"/>.
-        /// </summary>
-        /// <param name="args">The recycle args.</param>
-        public void RecycleElement(ElementFactoryRecycleArgs args);
-    }
-}

+ 0 - 67
src/Avalonia.Controls.ItemsRepeater/Controls/ItemTemplateWrapper.cs

@@ -1,67 +0,0 @@
-// This source file is adapted from the WinUI project.
-// (https://github.com/microsoft/microsoft-ui-xaml)
-//
-// Licensed to The Avalonia Project under MIT License, courtesy of The .NET Foundation.
-
-using Avalonia.Controls.Templates;
-
-namespace Avalonia.Controls
-{
-    internal class ItemTemplateWrapper : IElementFactory
-    {
-        private readonly IDataTemplate _dataTemplate;
-
-        public ItemTemplateWrapper(IDataTemplate dataTemplate) => _dataTemplate = dataTemplate;
-
-        public Control Build(object? param) => GetElement(null, param);
-        public bool Match(object? data) => _dataTemplate.Match(data);
-
-        public Control GetElement(ElementFactoryGetArgs args)
-        {
-            return GetElement(args.Parent, args.Data);
-        }
-
-        public void RecycleElement(ElementFactoryRecycleArgs args)
-        {
-            RecycleElement(args.Parent, args.Element!);
-        }
-
-        private Control GetElement(Control? parent, object? data)
-        {
-            var selectedTemplate = _dataTemplate;
-            var recyclePool = RecyclePool.GetPoolInstance(selectedTemplate);
-            Control? element = null;
-
-            if (recyclePool != null)
-            {
-                // try to get an element from the recycle pool.
-                element = recyclePool.TryGetElement(string.Empty, parent);
-            }
-
-            if (element == null)
-            {
-                // no element was found in recycle pool, create a new element
-                element = selectedTemplate.Build(data)!;
-
-                // Associate template with element
-                element.SetValue(RecyclePool.OriginTemplateProperty, selectedTemplate);
-            }
-
-            return element;
-        }
-
-        private void RecycleElement(Control? parent, Control element)
-        {
-            var selectedTemplate = _dataTemplate;
-            var recyclePool = RecyclePool.GetPoolInstance(selectedTemplate);
-            if (recyclePool == null)
-            {
-                // No Recycle pool in the template, create one.
-                recyclePool = new RecyclePool();
-                RecyclePool.SetPoolInstance(selectedTemplate, recyclePool);
-            }
-
-            recyclePool.PutElement(element, "" /* key */, parent);
-        }
-    }
-}

+ 0 - 792
src/Avalonia.Controls.ItemsRepeater/Controls/ItemsRepeater.cs

@@ -1,792 +0,0 @@
-// This source file is adapted from the WinUI project.
-// (https://github.com/microsoft/microsoft-ui-xaml)
-//
-// Licensed to The Avalonia Project under MIT License, courtesy of The .NET Foundation.
-
-using System;
-using System.Collections;
-using System.Collections.Specialized;
-using Avalonia.Controls.Templates;
-using Avalonia.Input;
-using Avalonia.Layout;
-using Avalonia.Logging;
-using Avalonia.LogicalTree;
-using Avalonia.Metadata;
-using Avalonia.Utilities;
-using Avalonia.VisualTree;
-
-namespace Avalonia.Controls
-{
-    /// <summary>
-    /// Represents a data-driven collection control that incorporates a flexible layout system,
-    /// custom views, and virtualization.
-    /// </summary>
-    public class ItemsRepeater : Panel, IChildIndexProvider
-    {
-        /// <summary>
-        /// Defines the <see cref="HorizontalCacheLength"/> property.
-        /// </summary>
-        public static readonly StyledProperty<double> HorizontalCacheLengthProperty =
-            AvaloniaProperty.Register<ItemsRepeater, double>(nameof(HorizontalCacheLength), 2.0);
-
-        /// <summary>
-        /// Defines the <see cref="ItemTemplate"/> property.
-        /// </summary>
-        public static readonly StyledProperty<IDataTemplate?> ItemTemplateProperty =
-            ItemsControl.ItemTemplateProperty.AddOwner<ItemsRepeater>();
-
-        /// <summary>
-        /// Defines the <see cref="ItemsSource"/> property.
-        /// </summary>
-        public static readonly DirectProperty<ItemsRepeater, IEnumerable?> ItemsSourceProperty =
-            AvaloniaProperty.RegisterDirect<ItemsRepeater, IEnumerable?>(
-                nameof(ItemsSource),
-                o => o.ItemsSource,
-                (o, v) => o.ItemsSource = v);
-
-        /// <summary>
-        /// Defines the <see cref="Layout"/> property.
-        /// </summary>
-        public static readonly StyledProperty<AttachedLayout?> LayoutProperty =
-            AvaloniaProperty.Register<ItemsRepeater, AttachedLayout?>(nameof(Layout), new StackLayout());
-
-        /// <summary>
-        /// Defines the <see cref="VerticalCacheLength"/> property.
-        /// </summary>
-        public static readonly StyledProperty<double> VerticalCacheLengthProperty =
-            AvaloniaProperty.Register<ItemsRepeater, double>(nameof(VerticalCacheLength), 2.0);
-
-        private static readonly StyledProperty<VirtualizationInfo?> VirtualizationInfoProperty =
-            AvaloniaProperty.RegisterAttached<ItemsRepeater, Control, VirtualizationInfo?>("VirtualizationInfo");
-
-        internal static readonly Rect InvalidRect = new Rect(-1, -1, -1, -1);
-        internal static readonly Point ClearedElementsArrangePosition = new Point(-10000.0, -10000.0);
-
-        private readonly ViewManager _viewManager;
-        private readonly ViewportManager _viewportManager;
-        private readonly TargetWeakEventSubscriber<ItemsRepeater, EventArgs> _layoutWeakSubscriber;
-        private IEnumerable? _itemsSource;
-        private RepeaterLayoutContext? _layoutContext;
-        private EventHandler<ChildIndexChangedEventArgs>? _childIndexChanged;
-        private bool _isLayoutInProgress;
-        private NotifyCollectionChangedEventArgs? _processingItemsSourceChange;
-        private ItemsRepeaterElementPreparedEventArgs? _elementPreparedArgs;
-        private ItemsRepeaterElementClearingEventArgs? _elementClearingArgs;
-        private ItemsRepeaterElementIndexChangedEventArgs? _elementIndexChangedArgs;
-
-        /// <summary>
-        /// Initializes a new instance of the <see cref="ItemsRepeater"/> class.
-        /// </summary>
-        public ItemsRepeater()
-        {
-            _layoutWeakSubscriber = new TargetWeakEventSubscriber<ItemsRepeater, EventArgs>(
-                this, static (target, _, ev, _) =>
-                {
-                    if (ev == AttachedLayout.ArrangeInvalidatedWeakEvent)
-                        target.InvalidateArrange();
-                    else if (ev == AttachedLayout.MeasureInvalidatedWeakEvent)
-                        target.InvalidateMeasure();
-                });
-
-            _viewManager = new ViewManager(this);
-            _viewportManager = new ViewportManager(this);
-            KeyboardNavigation.SetTabNavigation(this, KeyboardNavigationMode.Once);
-            OnLayoutChanged(null, Layout);
-        }
-
-        static ItemsRepeater()
-        {
-            ClipToBoundsProperty.OverrideDefaultValue<ItemsRepeater>(true);
-            RequestBringIntoViewEvent.AddClassHandler<ItemsRepeater>((x, e) => x.OnRequestBringIntoView(e));
-        }
-
-        /// <summary>
-        /// Gets or sets the layout used to size and position elements in the ItemsRepeater.
-        /// </summary>
-        /// <value>
-        /// The layout used to size and position elements. The default is a StackLayout with
-        /// vertical orientation.
-        /// </value>
-        public AttachedLayout? Layout
-        {
-            get => GetValue(LayoutProperty);
-            set => SetValue(LayoutProperty, value);
-        }
-
-        /// <summary>
-        /// Gets or sets an object source used to generate the content of the ItemsRepeater.
-        /// </summary>
-        public IEnumerable? ItemsSource
-        {
-            get => _itemsSource;
-            set => SetAndRaise(ItemsSourceProperty, ref _itemsSource, value);
-        }
-
-        /// <summary>
-        /// Gets or sets the template used to display each item.
-        /// </summary>
-        [InheritDataTypeFromItems(nameof(ItemsSource))]
-        public IDataTemplate? ItemTemplate
-        {
-            get => GetValue(ItemTemplateProperty);
-            set => SetValue(ItemTemplateProperty, value);
-        }
-
-        /// <summary>
-        /// Gets or sets a value that indicates the size of the buffer used to realize items when
-        /// panning or scrolling horizontally.
-        /// </summary>
-        public double HorizontalCacheLength
-        {
-            get => GetValue(HorizontalCacheLengthProperty);
-            set => SetValue(HorizontalCacheLengthProperty, value);
-        }
-
-        /// <summary>
-        /// Gets or sets a value that indicates the size of the buffer used to realize items when
-        /// panning or scrolling vertically.
-        /// </summary>
-        public double VerticalCacheLength
-        {
-            get => GetValue(VerticalCacheLengthProperty);
-            set => SetValue(VerticalCacheLengthProperty, value);
-        }
-
-        /// <summary>
-        /// Gets a standardized view of the supported interactions between a given Items object and
-        /// the ItemsRepeater control and its associated components.
-        /// </summary>
-        public ItemsSourceView? ItemsSourceView { get; private set; }
-
-        internal IElementFactory? ItemTemplateShim { get; set; }
-        internal Point LayoutOrigin { get; set; }
-        internal object? LayoutState { get; set; }
-        internal Control? MadeAnchor => _viewportManager.MadeAnchor;
-        internal Rect RealizationWindow => _viewportManager.GetLayoutRealizationWindow();
-        internal Control? SuggestedAnchor => _viewportManager.SuggestedAnchor;
-
-        private bool IsProcessingCollectionChange => _processingItemsSourceChange != null;
-
-        private RepeaterLayoutContext LayoutContext => _layoutContext ??= new RepeaterLayoutContext(this);
-
-        event EventHandler<ChildIndexChangedEventArgs>? IChildIndexProvider.ChildIndexChanged
-        {
-            add => _childIndexChanged += value;
-            remove => _childIndexChanged -= value;
-        }
-
-        int IChildIndexProvider.GetChildIndex(ILogical child)
-        {
-            return child is Control control
-                ? GetElementIndex(control)
-                : -1;
-        }
-
-        bool IChildIndexProvider.TryGetTotalCount(out int count)
-        {
-            count = ItemsSourceView?.Count ?? 0;
-            return true;
-        }
-
-        /// <summary>
-        /// Occurs each time an element is cleared and made available to be re-used.
-        /// </summary>
-        /// <remarks>
-        /// This event is raised immediately each time an element is cleared, such as when it falls
-        /// outside the range of realized items. Elements are cleared when they become available
-        /// for re-use.
-        /// </remarks>
-        public event EventHandler<ItemsRepeaterElementClearingEventArgs>? ElementClearing;
-
-        /// <summary>
-        /// Occurs for each realized <see cref="Control"/> when the index for the item it
-        /// represents has changed.
-        /// </summary>
-        /// <remarks>
-        /// When you use ItemsRepeater to build a more complex control that supports specific
-        /// interactions on the child elements (such as selection or click), it is useful to be
-        /// able to keep an up-to-date identifier for the backing data item.
-        ///
-        /// This event is raised for each realized Control where the index for the item it
-        /// represents has changed. For example, when another item is added or removed in the data
-        /// source, the index for items that come after in the ordering will be impacted.
-        /// </remarks>
-        public event EventHandler<ItemsRepeaterElementIndexChangedEventArgs>? ElementIndexChanged;
-
-        /// <summary>
-        /// Occurs each time an element is prepared for use.
-        /// </summary>
-        /// <remarks>
-        /// The prepared element might be newly created or an existing element that is being re-
-        /// used.
-        /// </remarks>
-        public event EventHandler<ItemsRepeaterElementPreparedEventArgs>? ElementPrepared;
-
-        /// <summary>
-        /// Retrieves the index of the item from the data source that corresponds to the specified
-        /// <see cref="Control"/>.
-        /// </summary>
-        /// <param name="element">
-        /// The element that corresponds to the item to get the index of.
-        /// </param>
-        /// <returns>
-        /// The index of the item from the data source that corresponds to the specified UIElement,
-        /// or -1 if the element is not supported.
-        /// </returns>
-        public int GetElementIndex(Control element) => GetElementIndexImpl(element);
-
-        /// <summary>
-        /// Retrieves the realized UIElement that corresponds to the item at the specified index in
-        /// the data source.
-        /// </summary>
-        /// <param name="index">The index of the item.</param>
-        /// <returns>
-        /// he UIElement that corresponds to the item at the specified index if the item is
-        /// realized, or null if the item is not realized.
-        /// </returns>
-        public Control? TryGetElement(int index) => GetElementFromIndexImpl(index);
-
-        /// <summary>
-        /// Retrieves the UIElement that corresponds to the item at the specified index in the
-        /// data source.
-        /// </summary>
-        /// <param name="index">The index of the item.</param>
-        /// <returns>
-        /// An <see cref="Control"/> that corresponds to the item at the specified index. If the
-        /// item is not realized, a new UIElement is created.
-        /// </returns>
-        public Control GetOrCreateElement(int index) => GetOrCreateElementImpl(index);
-
-        internal void PinElement(Control element) => _viewManager.UpdatePin(element, true);
-
-        internal void UnpinElement(Control element) => _viewManager.UpdatePin(element, false);
-
-        internal static VirtualizationInfo? TryGetVirtualizationInfo(Control? element)
-        {
-            return element?.GetValue(VirtualizationInfoProperty);
-        }
-
-        internal static VirtualizationInfo GetVirtualizationInfo(Control element)
-        {
-            var result = element.GetValue(VirtualizationInfoProperty);
-
-            if (result == null)
-            {
-                result = new VirtualizationInfo();
-                element.SetValue(VirtualizationInfoProperty, result);
-            }
-
-            return result;
-        }
-
-        private protected override void InvalidateMeasureOnChildrenChanged()
-        {
-            // Don't invalidate measure when children change.
-        }
-
-        /// <inheritdoc />
-        protected override Size MeasureOverride(Size availableSize)
-        {
-            if (_isLayoutInProgress)
-            {
-                throw new AvaloniaInternalException("Reentrancy detected during layout.");
-            }
-
-            if (IsProcessingCollectionChange)
-            {
-                throw new NotSupportedException("Cannot run layout in the middle of a collection change.");
-            }
-
-            _viewportManager.OnOwnerMeasuring();
-
-            _isLayoutInProgress = true;
-
-            try
-            {
-                _viewManager.PrunePinnedElements();
-                var extent = new Rect();
-                var desiredSize = new Size();
-                var layout = Layout;
-
-                if (layout != null)
-                {
-                    var layoutContext = LayoutContext;
-
-                    desiredSize = layout.Measure(layoutContext, availableSize);
-                    extent = new Rect(LayoutOrigin.X, LayoutOrigin.Y, desiredSize.Width, desiredSize.Height);
-
-                    // Clear auto recycle candidate elements that have not been kept alive by layout - i.e layout did not
-                    // call GetElementAt(index).
-                    foreach (var element in Children)
-                    {
-                        var virtInfo = GetVirtualizationInfo(element);
-
-                        if (virtInfo.Owner == ElementOwner.Layout &&
-                            virtInfo.AutoRecycleCandidate &&
-                            !virtInfo.KeepAlive)
-                        {
-                            Logger.TryGet(LogEventLevel.Verbose, "Repeater")?.Log(this, "AutoClear - {Index}", virtInfo.Index);
-                            ClearElementImpl(element);
-                        }
-                    }
-                }
-
-                _viewportManager.SetLayoutExtent(extent);
-                return desiredSize;
-            }
-            finally
-            {
-                _isLayoutInProgress = false;
-            }
-        }
-
-        /// <inheritdoc />
-        protected override Size ArrangeOverride(Size finalSize)
-        {
-            if (_isLayoutInProgress)
-            {
-                throw new AvaloniaInternalException("Reentrancy detected during layout.");
-            }
-
-            if (IsProcessingCollectionChange)
-            {
-                throw new NotSupportedException("Cannot run layout in the middle of a collection change.");
-            }
-
-            _isLayoutInProgress = true;
-
-            try
-            {
-                var arrangeSize = Layout?.Arrange(LayoutContext, finalSize) ?? default;
-
-                // The view manager might clear elements during this call.
-                // That's why we call it before arranging cleared elements
-                // off screen.
-                _viewManager.OnOwnerArranged();
-
-                foreach (var element in Children)
-                {
-                    var virtInfo = GetVirtualizationInfo(element);
-                    virtInfo.KeepAlive = false;
-
-                    if (virtInfo.Owner == ElementOwner.ElementFactory ||
-                        virtInfo.Owner == ElementOwner.PinnedPool)
-                    {
-                        // Toss it away. And arrange it with size 0 so that XYFocus won't use it.
-                        element.Arrange(new Rect(
-                            ClearedElementsArrangePosition.X - element.DesiredSize.Width,
-                            ClearedElementsArrangePosition.Y - element.DesiredSize.Height,
-                            0,
-                            0));
-                    }
-                    else
-                    {
-                        var newBounds = element.Bounds;
-                        virtInfo.ArrangeBounds = newBounds;
-
-                        _viewportManager.RegisterScrollAnchorCandidate(element, virtInfo);
-                    }
-                }
-
-                _viewportManager.OnOwnerArranged();
-
-                return arrangeSize;
-            }
-            finally
-            {
-                _isLayoutInProgress = false;
-            }
-        }
-
-        /// <inheritdoc />
-        protected override void OnAttachedToVisualTree(VisualTreeAttachmentEventArgs e)
-        {
-            base.OnAttachedToVisualTree(e);
-            InvalidateMeasure();
-            _viewportManager.ResetScrollers();
-        }
-
-        /// <inheritdoc />
-        protected override void OnDetachedFromVisualTree(VisualTreeAttachmentEventArgs e)
-        {
-            _viewportManager.ResetScrollers();
-        }
-
-        /// <inheritdoc />
-        protected override void OnPropertyChanged(AvaloniaPropertyChangedEventArgs change)
-        {
-            if (change.Property == ItemsSourceProperty)
-            {
-                var (oldEnumerable, newEnumerable) = change.GetOldAndNewValue<IEnumerable?>();
-
-                if (oldEnumerable != newEnumerable)
-                {
-                    var newDataSource = newEnumerable as ItemsSourceView;
-                    if (newEnumerable != null && newDataSource == null)
-                    {
-                        newDataSource = ItemsSourceView.GetOrCreate(newEnumerable);
-                    }
-
-                    OnDataSourcePropertyChanged(ItemsSourceView, newDataSource);
-                }
-            }
-            else if (change.Property == ItemTemplateProperty)
-            {
-                var (oldvalue, newValue) = change.GetOldAndNewValue<IDataTemplate?>();
-                OnItemTemplateChanged(oldvalue, newValue);
-            }
-            else if (change.Property == LayoutProperty)
-            {
-                var (oldvalue, newValue) = change.GetOldAndNewValue<AttachedLayout>();
-                OnLayoutChanged(oldvalue, newValue);
-            }
-            else if (change.Property == HorizontalCacheLengthProperty)
-            {
-                _viewportManager.HorizontalCacheLength = change.GetNewValue<double>();
-            }
-            else if (change.Property == VerticalCacheLengthProperty)
-            {
-                _viewportManager.VerticalCacheLength = change.GetNewValue<double>();
-            }
-
-            base.OnPropertyChanged(change);
-        }
-
-        internal Control GetElementImpl(int index, bool forceCreate, bool suppressAutoRecycle)
-        {
-            var element = _viewManager.GetElement(index, forceCreate, suppressAutoRecycle);
-            return element;
-        }
-
-        internal void ClearElementImpl(Control element)
-        {
-            // Clearing an element due to a collection change
-            // is more strict in that pinned elements will be forcibly
-            // unpinned and sent back to the view generator.
-            var isClearedDueToCollectionChange =
-                _processingItemsSourceChange != null &&
-                (_processingItemsSourceChange.Action == NotifyCollectionChangedAction.Remove ||
-                    _processingItemsSourceChange.Action == NotifyCollectionChangedAction.Replace ||
-                    _processingItemsSourceChange.Action == NotifyCollectionChangedAction.Reset);
-
-            _viewManager.ClearElement(element, isClearedDueToCollectionChange);
-            _viewportManager.OnElementCleared(element, GetVirtualizationInfo(element));
-        }
-
-        private int GetElementIndexImpl(Control element)
-        {
-            // Verify that element is actually a child of this ItemsRepeater
-            var parent = element.GetVisualParent();
-            
-            if (parent == this)
-            {
-                var virtInfo = TryGetVirtualizationInfo(element);
-                return _viewManager.GetElementIndex(virtInfo);
-            }
-
-            return -1;
-        }
-
-        private Control? GetElementFromIndexImpl(int index)
-        {
-            Control? result = null;
-
-            var children = Children;
-            for (var i = 0; i < children.Count && result == null; ++i)
-            {
-                var element = children[i];
-                var virtInfo = TryGetVirtualizationInfo(element);
-                if (virtInfo?.IsRealized == true && virtInfo.Index == index)
-                {
-                    result = element;
-                }
-            }
-
-            return result;
-        }
-
-        private Control GetOrCreateElementImpl(int index)
-        {
-            if (index >= 0 && index >= (ItemsSourceView?.Count ?? 0))
-            {
-                throw new ArgumentException("Argument index is invalid.", nameof(index));
-            }
-
-            if (_isLayoutInProgress)
-            {
-                throw new NotSupportedException("GetOrCreateElement invocation is not allowed during layout.");
-            }
-
-            var element = GetElementFromIndexImpl(index);
-            bool isAnchorOutsideRealizedRange = element == null;
-
-            if (isAnchorOutsideRealizedRange)
-            {
-                if (Layout == null)
-                {
-                    throw new InvalidOperationException("Cannot make an Anchor when there is no attached layout.");
-                }
-
-                element = (Control)LayoutContext.GetOrCreateElementAt(index);
-                element.Measure(Size.Infinity);
-            }
-
-            _viewportManager.OnMakeAnchor(element, isAnchorOutsideRealizedRange);
-            InvalidateMeasure();
-
-            return element!;
-        }
-
-        internal void OnElementPrepared(Control element, VirtualizationInfo virtInfo)
-        {
-            var index = virtInfo.Index;
-
-            _viewportManager.OnElementPrepared(element, virtInfo);
-
-            if (ElementPrepared != null)
-            {
-
-                if (_elementPreparedArgs == null)
-                {
-                    _elementPreparedArgs = new ItemsRepeaterElementPreparedEventArgs(element, index);
-                }
-                else
-                {
-                    _elementPreparedArgs.Update(element, index);
-                }
-
-                ElementPrepared(this, _elementPreparedArgs);
-            }
-
-            _childIndexChanged?.Invoke(this, new ChildIndexChangedEventArgs(element, index));
-        }
-
-        internal void OnElementClearing(Control element)
-        {
-            if (ElementClearing != null)
-            {
-                if (_elementClearingArgs == null)
-                {
-                    _elementClearingArgs = new ItemsRepeaterElementClearingEventArgs(element);
-                }
-                else
-                {
-                    _elementClearingArgs.Update(element);
-                }
-
-                ElementClearing(this, _elementClearingArgs);
-            }
-
-            _childIndexChanged?.Invoke(this, new ChildIndexChangedEventArgs(element, -1));
-        }
-
-        internal void OnElementIndexChanged(Control element, int oldIndex, int newIndex)
-        {
-            if (ElementIndexChanged != null)
-            {
-                if (_elementIndexChangedArgs == null)
-                {
-                    _elementIndexChangedArgs = new ItemsRepeaterElementIndexChangedEventArgs(element, oldIndex, newIndex);
-                }
-                else
-                {
-                    _elementIndexChangedArgs.Update(element, oldIndex, newIndex);
-                }
-
-                ElementIndexChanged(this, _elementIndexChangedArgs);
-            }
-
-            _childIndexChanged?.Invoke(this, new ChildIndexChangedEventArgs(element, newIndex));
-        }
-
-        private void OnDataSourcePropertyChanged(ItemsSourceView? oldValue, ItemsSourceView? newValue)
-        {
-            if (_isLayoutInProgress)
-            {
-                throw new AvaloniaInternalException("Cannot set ItemsSourceView during layout.");
-            }
-
-            if (oldValue != null)
-            {
-                oldValue.CollectionChanged -= OnItemsSourceViewChanged;
-            }
-
-            ItemsSourceView = newValue;
-
-            if (newValue != null)
-            {
-                newValue.CollectionChanged += OnItemsSourceViewChanged;
-            }
-
-            if (Layout != null)
-            {
-                var args = new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Reset);
-
-                try
-                {
-                    _processingItemsSourceChange = args;
-
-                    if (Layout is VirtualizingLayout virtualLayout)
-                    {
-                        virtualLayout.OnItemsChanged(LayoutContext, newValue, args);
-                    }
-                    else if (Layout is NonVirtualizingLayout)
-                    {
-                        // Walk through all the elements and make sure they are cleared for
-                        // non-virtualizing layouts.
-                        foreach (var element in Children)
-                        {
-                            if (GetVirtualizationInfo(element).IsRealized)
-                            {
-                                ClearElementImpl(element);
-                            }
-                        }
-
-                        Children.Clear();
-                    }
-                }
-                finally
-                {
-                    _processingItemsSourceChange = null;
-                }
-
-                InvalidateMeasure();
-            }
-        }
-
-        private void OnItemTemplateChanged(IDataTemplate? oldValue, IDataTemplate? newValue)
-        {
-            if (_isLayoutInProgress && oldValue != null)
-            {
-                throw new AvaloniaInternalException("ItemTemplate cannot be changed during layout.");
-            }
-
-            // Since the ItemTemplate has changed, we need to re-evaluate all the items that
-            // have already been created and are now in the tree. The easiest way to do that
-            // would be to do a reset.. Note that this has to be done before we change the template
-            // so that the cleared elements go back into the old template.
-            if (Layout != null)
-            {
-                if (Layout is VirtualizingLayout virtualLayout)
-                {
-                    var args = new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Reset);
-                    _processingItemsSourceChange = args;
-
-                    try
-                    {
-                        virtualLayout.OnItemsChanged(LayoutContext, newValue, args);
-                    }
-                    finally
-                    {
-                        _processingItemsSourceChange = null;
-                    }
-                }
-                else if (Layout is NonVirtualizingLayout)
-                {
-                    // Walk through all the elements and make sure they are cleared for
-                    // non-virtualizing layouts.
-                    foreach (var element in Children)
-                    {
-                        if (GetVirtualizationInfo(element).IsRealized)
-                        {
-                            ClearElementImpl(element);
-                        }
-                    }
-                }
-            }
-
-            ItemTemplateShim = newValue switch
-            {
-                IElementFactory factory => factory,
-                null => null,
-                _ => new ItemTemplateWrapper(newValue)
-            };
-
-            InvalidateMeasure();
-        }
-
-        private void OnLayoutChanged(AttachedLayout? oldValue, AttachedLayout? newValue)
-        {
-            if (_isLayoutInProgress)
-            {
-                throw new InvalidOperationException("Layout cannot be changed during layout.");
-            }
-
-            _viewManager.OnLayoutChanging();
-
-            if (oldValue != null)
-            {
-                oldValue.UninitializeForContext(LayoutContext);
-
-                AttachedLayout.MeasureInvalidatedWeakEvent.Unsubscribe(oldValue, _layoutWeakSubscriber);
-                AttachedLayout.ArrangeInvalidatedWeakEvent.Unsubscribe(oldValue, _layoutWeakSubscriber);
-
-                // Walk through all the elements and make sure they are cleared
-                foreach (var element in Children)
-                {
-                    if (GetVirtualizationInfo(element).IsRealized)
-                    {
-                        ClearElementImpl(element);
-                    }
-                }
-
-                LayoutState = null;
-            }
-
-            if (newValue != null)
-            {
-                newValue.InitializeForContext(LayoutContext);
-
-                AttachedLayout.MeasureInvalidatedWeakEvent.Subscribe(newValue, _layoutWeakSubscriber);
-                AttachedLayout.ArrangeInvalidatedWeakEvent.Subscribe(newValue, _layoutWeakSubscriber);
-            }
-
-            bool isVirtualizingLayout = newValue is VirtualizingLayout;
-            _viewportManager.OnLayoutChanged(isVirtualizingLayout);
-            InvalidateMeasure();
-        }
-
-        private void OnItemsSourceViewChanged(object? sender, NotifyCollectionChangedEventArgs args)
-        {
-            if (_isLayoutInProgress)
-            {
-                // Bad things will follow if the data changes while we are in the middle of a layout pass.
-                throw new InvalidOperationException("Changes in data source are not allowed during layout.");
-            }
-
-            if (IsProcessingCollectionChange)
-            {
-                throw new InvalidOperationException("Changes in the data source are not allowed during another change in the data source.");
-            }
-
-            _processingItemsSourceChange = args;
-
-            try
-            {
-                _viewManager.OnItemsSourceChanged(sender, args);
-
-                if (Layout != null)
-                {
-                    if (Layout is VirtualizingLayout virtualLayout)
-                    {
-                        virtualLayout.OnItemsChanged(LayoutContext, sender, args);
-                    }
-                    else
-                    {
-                        // NonVirtualizingLayout
-                        InvalidateMeasure();
-                    }
-                }
-            }
-            finally
-            {
-                _processingItemsSourceChange = null;
-            }
-        }
-
-        private void OnRequestBringIntoView(RequestBringIntoViewEventArgs e)
-        {
-            _viewportManager.OnBringIntoViewRequested(e);
-        }
-    }
-}

+ 0 - 24
src/Avalonia.Controls.ItemsRepeater/Controls/ItemsRepeaterElementClearingEventArgs.cs

@@ -1,24 +0,0 @@
-// This source file is adapted from the WinUI project.
-// (https://github.com/microsoft/microsoft-ui-xaml)
-//
-// Licensed to The Avalonia Project under MIT License, courtesy of The .NET Foundation.
-
-using System;
-
-namespace Avalonia.Controls
-{
-    /// <summary>
-    /// Provides data for the <see cref="ItemsRepeater.ElementClearing"/> event.
-    /// </summary>
-    public class ItemsRepeaterElementClearingEventArgs : EventArgs
-    {
-        internal ItemsRepeaterElementClearingEventArgs(Control element) => Element = element;
-
-        /// <summary>
-        /// Gets the element that is being cleared for re-use.
-        /// </summary>
-        public Control Element { get; private set; }
-
-        internal void Update(Control element) => Element = element;
-    }
-}

+ 0 - 44
src/Avalonia.Controls.ItemsRepeater/Controls/ItemsRepeaterElementIndexChangedEventArgs.cs

@@ -1,44 +0,0 @@
-// This source file is adapted from the WinUI project.
-// (https://github.com/microsoft/microsoft-ui-xaml)
-//
-// Licensed to The Avalonia Project under MIT License, courtesy of The .NET Foundation.
-
-using System;
-
-namespace Avalonia.Controls
-{
-    /// <summary>
-    /// Provides data for the <see cref="ItemsRepeater.ElementIndexChanged"/> event.
-    /// </summary>
-    public class ItemsRepeaterElementIndexChangedEventArgs : EventArgs
-    {
-        internal ItemsRepeaterElementIndexChangedEventArgs(Control element, int oldIndex, int newIndex)
-        {
-            Element = element;
-            OldIndex = oldIndex;
-            NewIndex = newIndex;
-        }
-
-        /// <summary>
-        /// Get the element for which the index changed.
-        /// </summary>
-        public Control Element { get; private set; }
-
-        /// <summary>
-        /// Gets the index of the element after the change.
-        /// </summary>
-        public int NewIndex { get; private set; }
-
-        /// <summary>
-        /// Gets the index of the element before the change.
-        /// </summary>
-        public int OldIndex { get; private set; }
-
-        internal void Update(Control element, int oldIndex, int newIndex)
-        {
-            Element = element;
-            NewIndex = newIndex;
-            OldIndex = oldIndex;
-        }
-    }
-}

+ 0 - 35
src/Avalonia.Controls.ItemsRepeater/Controls/ItemsRepeaterElementPreparedEventArgs.cs

@@ -1,35 +0,0 @@
-// This source file is adapted from the WinUI project.
-// (https://github.com/microsoft/microsoft-ui-xaml)
-//
-// Licensed to The Avalonia Project under MIT License, courtesy of The .NET Foundation.
-
-namespace Avalonia.Controls
-{
-    /// <summary>
-    /// Provides data for the <see cref="ItemsRepeater.ElementPrepared"/> event.
-    /// </summary>
-    public class ItemsRepeaterElementPreparedEventArgs
-    {
-        internal ItemsRepeaterElementPreparedEventArgs(Control element, int index)
-        {
-            Element = element;
-            Index = index;
-        }
-
-        /// <summary>
-        /// Gets the prepared element.
-        /// </summary>
-        public Control Element { get; private set; }
-
-        /// <summary>
-        /// Gets the index of the item the element was prepared for.
-        /// </summary>
-        public int Index { get; private set; }
-
-        internal void Update(Control element, int index)
-        {
-            Element = element;
-            Index = index;
-        }
-    }
-}

+ 0 - 112
src/Avalonia.Controls.ItemsRepeater/Controls/RecyclePool.cs

@@ -1,112 +0,0 @@
-// This source file is adapted from the WinUI project.
-// (https://github.com/microsoft/microsoft-ui-xaml)
-//
-// Licensed to The Avalonia Project under MIT License, courtesy of The .NET Foundation.
-
-using System;
-using System.Collections.Generic;
-using System.Linq;
-using System.Runtime.CompilerServices;
-using Avalonia.Controls.Templates;
-
-namespace Avalonia.Controls
-{
-    public class RecyclePool
-    {
-        internal static readonly AttachedProperty<IDataTemplate> OriginTemplateProperty =
-            AvaloniaProperty.RegisterAttached<RecyclePool, Control, IDataTemplate>("OriginTemplate");
-
-        internal static readonly AttachedProperty<string> ReuseKeyProperty =
-            AvaloniaProperty.RegisterAttached<RecyclePool, Control, string>("ReuseKey", string.Empty);
-
-        private static ConditionalWeakTable<IDataTemplate, RecyclePool> s_pools = new ConditionalWeakTable<IDataTemplate, RecyclePool>();
-        private readonly Dictionary<string, List<ElementInfo>> _elements = new Dictionary<string, List<ElementInfo>>();
-
-        public static RecyclePool? GetPoolInstance(IDataTemplate dataTemplate)
-        {
-            s_pools.TryGetValue(dataTemplate, out var result);
-            return result;
-        }
-
-        public static void SetPoolInstance(IDataTemplate dataTemplate, RecyclePool value) => s_pools.Add(dataTemplate, value);
-
-        public void PutElement(Control element, string key, Control? owner)
-        {
-            var ownerAsPanel = EnsureOwnerIsPanelOrNull(owner);
-            var elementInfo = new ElementInfo(element, ownerAsPanel);
-
-            if (!_elements.TryGetValue(key, out var pool))
-            {
-                pool = new List<ElementInfo>();
-                _elements.Add(key, pool);
-            }
-
-            pool.Add(elementInfo);
-        }
-
-        public Control? TryGetElement(string key, Control? owner)
-        {
-            if (_elements.TryGetValue(key, out var elements))
-            {
-                if (elements.Count > 0)
-                {
-                    // Prefer an element from the same owner or with no owner so that we don't incur
-                    // the enter/leave cost during recycling.
-                    // TODO: prioritize elements with the same owner to those without an owner.
-                    var elementInfo = elements.FirstOrDefault(x => x.Owner == owner) ?? elements.LastOrDefault();
-                    elements.Remove(elementInfo!);
-
-                    var ownerAsPanel = EnsureOwnerIsPanelOrNull(owner);
-                    if (elementInfo!.Owner != null && elementInfo.Owner != ownerAsPanel)
-                    {
-                        // Element is still under its parent. remove it from its parent.
-                        var panel = elementInfo.Owner;
-                        if (panel != null)
-                        {
-                            int childIndex = panel.Children.IndexOf(elementInfo.Element);
-                            if (childIndex == -1)
-                            {
-                                throw new KeyNotFoundException("ItemsRepeater's child not found in its Children collection.");
-                            }
-
-                            panel.Children.RemoveAt(childIndex);
-                        }
-                    }
-
-                    return elementInfo.Element;
-                }
-            }
-
-            return null;
-        }
-
-        internal string GetReuseKey(Control element) => element.GetValue(ReuseKeyProperty);
-        internal void SetReuseKey(Control element, string value) => element.SetValue(ReuseKeyProperty, value);
-
-        private Panel? EnsureOwnerIsPanelOrNull(Control? owner)
-        {
-            if (owner is Panel panel)
-            {
-                return panel;
-            }
-            else if (owner != null)
-            {
-                throw new InvalidOperationException("Owner must be IPanel or null.");
-            }
-
-            return null;
-        }
-
-        private class ElementInfo
-        {
-            public ElementInfo(Control element, Panel? owner)
-            {
-                Element = element;
-                Owner = owner;
-            }
-            
-            public Control Element { get; }
-            public Panel? Owner { get;}
-        }
-    }
-}

+ 0 - 117
src/Avalonia.Controls.ItemsRepeater/Controls/RecyclingElementFactory.cs

@@ -1,117 +0,0 @@
-using System;
-using System.Collections.Generic;
-using System.Linq;
-using Avalonia.Controls.Templates;
-
-namespace Avalonia.Controls
-{
-    public class SelectTemplateEventArgs : EventArgs
-    {
-        public string? TemplateKey { get; set; }
-        public object? DataContext { get; internal set; }
-        public Control? Owner { get; internal set; }
-    }
-
-    public class RecyclingElementFactory : ElementFactory
-    {
-        private RecyclePool? _recyclePool;
-        private IDictionary<string, IDataTemplate>? _templates;
-        private SelectTemplateEventArgs? _args;
-
-        public RecyclingElementFactory()
-        {
-            Templates = new Dictionary<string, IDataTemplate>();
-        }
-
-        public RecyclePool RecyclePool 
-        {
-            get => _recyclePool ??= new RecyclePool();
-            set => _recyclePool = value ?? throw new ArgumentNullException(nameof(value));
-        }
-
-        public IDictionary<string, IDataTemplate> Templates 
-        {
-            get => _templates ??= new Dictionary<string, IDataTemplate>();
-            set => _templates = value ?? throw new ArgumentNullException(nameof(value));
-        }
-
-        public event EventHandler<SelectTemplateEventArgs>? SelectTemplateKey;
-
-        protected override Control GetElementCore(ElementFactoryGetArgs args)
-        {
-            if (_templates == null || _templates.Count == 0)
-            {
-                throw new InvalidOperationException("Templates cannot be empty.");
-            }
-
-            var templateKey = Templates.Count == 1 ?
-                Templates.First().Key :
-                OnSelectTemplateKeyCore(args.Data, args.Parent);
-
-            if (string.IsNullOrEmpty(templateKey))
-            {
-                // Note: We could allow null/whitespace, which would work as long as
-                // the recycle pool is not shared. in order to make this work in all cases
-                // currently we validate that a valid template key is provided.
-                throw new InvalidOperationException("Template key cannot be null or empty.");
-            }
-
-            // Get an element from the Recycle Pool or create one
-            var element = RecyclePool.TryGetElement(templateKey, args.Parent);
-
-            if (element is null)
-            {
-                // No need to call HasKey if there is only one template.
-                if (Templates.Count > 1 && !Templates.ContainsKey(templateKey))
-                {
-                    var message = $"No templates of key '{templateKey}' were found in the templates collection.";
-                    throw new InvalidOperationException(message);
-                }
-
-                var dataTemplate = Templates[templateKey];
-                element = dataTemplate.Build(args.Data)!;
-
-                // Associate ReuseKey with element
-                RecyclePool.SetReuseKey(element, templateKey);
-            }
-
-            return element;
-        }
-
-        protected override void RecycleElementCore(ElementFactoryRecycleArgs args)
-        {
-            var element = args.Element!;
-            var key = RecyclePool.GetReuseKey(element);
-            RecyclePool.PutElement(element, key, args.Parent);
-        }
-
-        protected virtual string OnSelectTemplateKeyCore(object? dataContext, Control? owner)
-        {
-            if (SelectTemplateKey is not null)
-            {
-                _args ??= new SelectTemplateEventArgs();
-                _args.TemplateKey = null;
-                _args.DataContext = dataContext;
-                _args.Owner = owner;
-
-                try
-                {
-                    SelectTemplateKey(this, _args);
-                }
-                finally
-                {
-                    _args.DataContext = null;
-                    _args.Owner = null;
-                }
-            }
-
-            if (string.IsNullOrEmpty(_args?.TemplateKey))
-            {
-                throw new InvalidOperationException(
-                    "Please provide a valid template identifier in the handler for the SelectTemplateKey event.");
-            }
-
-            return _args!.TemplateKey!;
-        }
-    }
-}

+ 0 - 70
src/Avalonia.Controls.ItemsRepeater/Controls/RepeaterLayoutContext.cs

@@ -1,70 +0,0 @@
-// This source file is adapted from the WinUI project.
-// (https://github.com/microsoft/microsoft-ui-xaml)
-//
-// Licensed to The Avalonia Project under MIT License, courtesy of The .NET Foundation.
-
-using System;
-using System.Collections.Generic;
-using System.Text;
-using Avalonia.Layout;
-using Avalonia.Logging;
-
-namespace Avalonia.Controls
-{
-    internal class RepeaterLayoutContext : VirtualizingLayoutContext
-    {
-        private readonly ItemsRepeater _owner;
-
-        public RepeaterLayoutContext(ItemsRepeater owner)
-        {
-            _owner = owner;
-        }
-
-        protected override Point LayoutOriginCore
-        {
-            get => _owner.LayoutOrigin;
-            set => _owner.LayoutOrigin = value;
-        }
-
-        protected override object? LayoutStateCore
-        {
-            get => _owner.LayoutState;
-            set => _owner.LayoutState = value;
-        }
-
-        protected override int RecommendedAnchorIndexCore
-        {
-            get
-            {
-                int anchorIndex = -1;
-                var anchor = _owner.SuggestedAnchor;
-                if (anchor != null)
-                {
-                    anchorIndex = _owner.GetElementIndex(anchor);
-                }
-
-                return anchorIndex;
-            }
-        }
-
-        protected override int ItemCountCore() => _owner.ItemsSourceView?.Count ?? 0;
-
-        protected override Layoutable GetOrCreateElementAtCore(int index, ElementRealizationOptions options)
-        {
-            return _owner.GetElementImpl(
-                index,
-                options.HasFlag(ElementRealizationOptions.ForceCreate),
-                options.HasFlag(ElementRealizationOptions.SuppressAutoRecycle));
-        }
-
-        protected override object GetItemAtCore(int index) => _owner.ItemsSourceView!.GetAt(index)!;
-
-        protected override void RecycleElementCore(Layoutable element)
-        {
-            Logger.TryGet(LogEventLevel.Verbose, "Repeater")?.Log(this, "RepeaterLayout - RecycleElement: {Index}", _owner.GetElementIndex((Control)element));
-            _owner.ClearElementImpl((Control)element);
-        }
-
-        protected override Rect RealizationRectCore() => _owner.RealizationWindow;
-    }
-}

+ 0 - 54
src/Avalonia.Controls.ItemsRepeater/Controls/UniqueIdElementPool.cs

@@ -1,54 +0,0 @@
-// This source file is adapted from the WinUI project.
-// (https://github.com/microsoft/microsoft-ui-xaml)
-//
-// Licensed to The Avalonia Project under MIT License, courtesy of The .NET Foundation.
-
-using System;
-using System.Collections;
-using System.Collections.Generic;
-using System.Text;
-
-namespace Avalonia.Controls
-{
-    internal class UniqueIdElementPool : IEnumerable<KeyValuePair<string, Control>>
-    {
-        private readonly Dictionary<string, Control> _elementMap = new Dictionary<string, Control>();
-        private readonly ItemsRepeater _owner;
-
-        public UniqueIdElementPool(ItemsRepeater owner) => _owner = owner;
-
-        public void Add(Control element)
-        {
-            var virtInfo = ItemsRepeater.GetVirtualizationInfo(element);
-            var key = virtInfo.UniqueId!;
-
-            if (_elementMap.ContainsKey(key))
-            {
-                throw new InvalidOperationException($"The unique id provided ({key}) is not unique.");
-            }
-
-            _elementMap.Add(key, element);
-        }
-
-        public Control? Remove(int index)
-        {
-            // Check if there is already a element in the mapping and if so, use it.
-            string key = _owner.ItemsSourceView!.KeyFromIndex(index);
-
-            if (_elementMap.TryGetValue(key, out var element))
-            {
-                _elementMap.Remove(key);
-            }
-
-            return element;
-        }
-
-        public void Clear()
-        {
-            _elementMap.Clear();
-        }
-
-        public IEnumerator<KeyValuePair<string, Control>> GetEnumerator() => _elementMap.GetEnumerator();
-        IEnumerator IEnumerable.GetEnumerator() => GetEnumerator();
-    }
-}

+ 0 - 782
src/Avalonia.Controls.ItemsRepeater/Controls/ViewManager.cs

@@ -1,782 +0,0 @@
-// This source file is adapted from the WinUI project.
-// (https://github.com/microsoft/microsoft-ui-xaml)
-//
-// Licensed to The Avalonia Project under MIT License, courtesy of The .NET Foundation.
-
-using System;
-using System.Collections.Generic;
-using System.Collections.Specialized;
-using Avalonia.Controls.Templates;
-using Avalonia.Input;
-using Avalonia.Interactivity;
-using Avalonia.Logging;
-using Avalonia.VisualTree;
-
-namespace Avalonia.Controls
-{
-    internal sealed class ViewManager
-    {
-        private const int FirstRealizedElementIndexDefault = int.MaxValue;
-        private const int LastRealizedElementIndexDefault = int.MinValue;
-
-        private readonly ItemsRepeater _owner;
-        private readonly List<PinnedElementInfo> _pinnedPool = new List<PinnedElementInfo>();
-        private readonly UniqueIdElementPool _resetPool;
-        private Control? _lastFocusedElement;
-        private bool _isDataSourceStableResetPending;
-        private ElementFactoryGetArgs? _elementFactoryGetArgs;
-        private ElementFactoryRecycleArgs? _elementFactoryRecycleArgs;
-        private int _firstRealizedElementIndexHeldByLayout = FirstRealizedElementIndexDefault;
-        private int _lastRealizedElementIndexHeldByLayout = LastRealizedElementIndexDefault;
-        private bool _eventsSubscribed;
-
-        public ViewManager(ItemsRepeater owner)
-        {
-            _owner = owner;
-            _resetPool = new UniqueIdElementPool(owner);
-        }
-
-        public Control GetElement(int index, bool forceCreate, bool suppressAutoRecycle)
-        {
-            var element = forceCreate ? null : GetElementIfAlreadyHeldByLayout(index);
-            if (element == null)
-            {
-                // check if this is the anchor made through repeater in preparation 
-                // for a bring into view.
-                var madeAnchor = _owner.MadeAnchor;
-                if (madeAnchor != null)
-                {
-                    var anchorVirtInfo = ItemsRepeater.TryGetVirtualizationInfo(madeAnchor);
-                    if (anchorVirtInfo!.Index == index)
-                    {
-                        element = madeAnchor;
-                    }
-                }
-            }
-            if (element == null) { element = GetElementFromUniqueIdResetPool(index); }
-            if (element == null) { element = GetElementFromPinnedElements(index); }
-            if (element == null) { element = GetElementFromElementFactory(index); }
-
-            var virtInfo = ItemsRepeater.TryGetVirtualizationInfo(element);
-            if (suppressAutoRecycle)
-            {
-                virtInfo!.AutoRecycleCandidate = false;
-                Logger.TryGet(LogEventLevel.Verbose, "Repeater")?.Log(this, "GetElement: {Index} Not AutoRecycleCandidate:", virtInfo.Index);
-            }
-            else
-            {
-                virtInfo!.AutoRecycleCandidate = true;
-                virtInfo.KeepAlive = true;
-                Logger.TryGet(LogEventLevel.Verbose, "Repeater")?.Log(this, "GetElement: {Index} AutoRecycleCandidate:", virtInfo.Index);
-            }
-
-            return element;
-        }
-
-        public void ClearElement(Control element, bool isClearedDueToCollectionChange)
-        {
-            var virtInfo = ItemsRepeater.GetVirtualizationInfo(element);
-            var index = virtInfo.Index;
-            bool cleared =
-                ClearElementToUniqueIdResetPool(element, virtInfo) ||
-                ClearElementToPinnedPool(element, virtInfo, isClearedDueToCollectionChange);
-
-            if (!cleared)
-            {
-                ClearElementToElementFactory(element);
-            }
-
-            // Both First and Last indices need to be valid or default.
-            if (index == _firstRealizedElementIndexHeldByLayout && index == _lastRealizedElementIndexHeldByLayout)
-            {
-                // First and last were pointing to the same element and that is going away.
-                InvalidateRealizedIndicesHeldByLayout();
-            }
-            else if (index == _firstRealizedElementIndexHeldByLayout)
-            {
-                // The FirstElement is going away, shrink the range by one.
-                ++_firstRealizedElementIndexHeldByLayout;
-            }
-            else if (index == _lastRealizedElementIndexHeldByLayout)
-            {
-                // Last element is going away, shrink the range by one at the end.
-                --_lastRealizedElementIndexHeldByLayout;
-            }
-            else
-            {
-                // Index is either outside the range we are keeping track of or inside the range.
-                // In both these cases, we just keep the range we have. If this clear was due to 
-                // a collection change, then in the CollectionChanged event, we will invalidate these guys.
-            }
-        }
-
-        // We need to clear the datacontext to prevent crashes from happening,
-        //  however we only do that if we were the ones setting it.
-        // That is when one of the following is the case (numbering taken from line ~642):
-        // 1.2    No ItemTemplate, data is not a UIElement
-        // 2.1    ItemTemplate, data is not FrameworkElement
-        // 2.2.2  Itemtemplate, data is FrameworkElement, ElementFactory returned Element different to data
-        //
-        // In all of those three cases, we the ItemTemplateShim is NOT null.
-        // Luckily when we create the items, we store whether we were the once setting the DataContext.
-        public void ClearElementToElementFactory(Control element)
-        {
-            _owner.OnElementClearing(element);
-
-            var virtInfo = ItemsRepeater.GetVirtualizationInfo(element);
-            virtInfo.MoveOwnershipToElementFactory();
-
-            // During creation of this object, we were the one setting the DataContext, so clear it now.
-            if (virtInfo.MustClearDataContext)
-            {
-                element.DataContext = null;
-            }
-
-            if (_owner.ItemTemplateShim != null)
-            {
-                var context = _elementFactoryRecycleArgs ??= new ElementFactoryRecycleArgs();
-                context.Element = element;
-                context.Parent = _owner;
-
-                _owner.ItemTemplateShim.RecycleElement(context);
-
-                context.Element = null;
-                context.Parent = null;
-            }
-            else
-            {
-                // No ItemTemplate to recycle to, remove the element from the children collection.
-                if (!_owner.Children.Remove(element))
-                {
-                    throw new InvalidOperationException("ItemsRepeater's child not found in its Children collection.");
-                }
-            }
-
-            if (_lastFocusedElement == element)
-            {
-                // Focused element is going away. Remove the tracked last focused element
-                // and pick a reasonable next focus if we can find one within the layout 
-                // realized elements.
-                MoveFocusFromClearedIndex(virtInfo.Index);
-            }
-        }
-
-        private void MoveFocusFromClearedIndex(int clearedIndex)
-        {
-            var focusCandidate = FindFocusCandidate(clearedIndex, out var focusedChild);
-            if (focusCandidate != null)
-            {
-                focusCandidate.Focus();
-                _lastFocusedElement = focusedChild;
-
-                // Add pin to hold the focused element.
-                UpdatePin(focusedChild!, true /* addPin */);
-            }
-            else
-            {
-                // We could not find a candidate.
-                _lastFocusedElement = null;
-            }
-        }
-
-        Control? FindFocusCandidate(int clearedIndex, out Control? focusedChild)
-        {
-            // Walk through all the children and find elements with index before and after the cleared index.
-            // Note that during a delete the next element would now have the same index.
-            int previousIndex = int.MinValue;
-            int nextIndex = int.MaxValue;
-            Control? nextElement = null;
-            Control? previousElement = null;
-
-            foreach (var child in _owner.Children)
-            {
-                var virtInfo = ItemsRepeater.TryGetVirtualizationInfo(child);
-                if (virtInfo?.IsHeldByLayout == true)
-                {
-                    int currentIndex = virtInfo.Index;
-                    if (currentIndex < clearedIndex)
-                    {
-                        if (currentIndex > previousIndex)
-                        {
-                            previousIndex = currentIndex;
-                            previousElement = child;
-                        }
-                    }
-                    else if (currentIndex >= clearedIndex)
-                    {
-                        // Note that we use >= above because if we deleted the focused element, 
-                        // the next element would have the same index now.
-                        if (currentIndex < nextIndex)
-                        {
-                            nextIndex = currentIndex;
-                            nextElement = child;
-                        }
-                    }
-                }
-            }
-
-            // TODO: Find the next element if one exists, if not use the previous element.
-            // If the container itself is not focusable, find a descendent that is.
-            focusedChild = nextElement;
-            return nextElement;
-        }
-
-        public int GetElementIndex(VirtualizationInfo? virtInfo)
-        {
-            if (virtInfo == null)
-            {
-                //Element is not a child of this ItemsRepeater.
-                return -1;
-            }
-
-            return virtInfo.IsRealized || virtInfo.IsInUniqueIdResetPool ? virtInfo.Index : -1;
-        }
-
-        public void PrunePinnedElements()
-        {
-            EnsureEventSubscriptions();
-
-            // Go through pinned elements and make sure they still have
-            // a reason to be pinned.
-            for (var i = 0; i < _pinnedPool.Count; ++i)
-            {
-                var elementInfo = _pinnedPool[i];
-                var virtInfo = elementInfo.VirtualizationInfo;
-
-                if (!virtInfo.IsPinned)
-                {
-                    _pinnedPool.RemoveAt(i);
-                    --i;
-
-                    // Pinning was the only thing keeping this element alive.
-                    ClearElementToElementFactory(elementInfo.PinnedElement);
-                }
-            }
-        }
-
-        public void UpdatePin(Control element, bool addPin)
-        {
-            var parent = element.GetVisualParent();
-            var child = (Visual)element;
-
-            while (parent != null)
-            {
-                if (parent is ItemsRepeater repeater)
-                {
-                    var virtInfo = ItemsRepeater.GetVirtualizationInfo((Control)child);
-                    if (virtInfo.IsRealized)
-                    {
-                        if (addPin)
-                        {
-                            virtInfo.AddPin();
-                        }
-                        else if (virtInfo.IsPinned)
-                        {
-                            if (virtInfo.RemovePin() == 0)
-                            {
-                                // ElementFactory is invoked during the measure pass.
-                                // We will clear the element then.
-                                repeater.InvalidateMeasure();
-                            }
-                        }
-                    }
-                }
-
-                child = parent;
-                parent = child.GetVisualParent();
-            }
-        }
-
-        public void OnItemsSourceChanged(object? sender, NotifyCollectionChangedEventArgs args)
-        {
-            // Note: For items that have been removed, the index will not be touched. It will hold
-            // the old index before it was removed. It is not valid anymore.
-            switch (args.Action)
-            {
-                case NotifyCollectionChangedAction.Add:
-                    {
-                        var newIndex = args.NewStartingIndex;
-                        var newCount = args.NewItems!.Count;
-                        EnsureFirstLastRealizedIndices();
-                        if (newIndex <= _lastRealizedElementIndexHeldByLayout)
-                        {
-                            _lastRealizedElementIndexHeldByLayout += newCount;
-                            foreach (var element in _owner.Children)
-                            {
-                                var virtInfo = ItemsRepeater.GetVirtualizationInfo(element);
-                                var dataIndex = virtInfo.Index;
-
-                                if (virtInfo.IsRealized && dataIndex >= newIndex)
-                                {
-                                    UpdateElementIndex(element, virtInfo, dataIndex + newCount);
-                                }
-                            }
-                        }
-                        else
-                        {
-                            // Indices held by layout are not affected
-                            // We could still have items in the pinned elements that need updates. This is usually a very small vector.
-                            for (var i = 0; i < _pinnedPool.Count; ++i)
-                            {
-                                var elementInfo = _pinnedPool[i];
-                                var virtInfo = elementInfo.VirtualizationInfo;
-                                var dataIndex = virtInfo.Index;
-
-                                if (virtInfo.IsRealized && dataIndex >= newIndex)
-                                {
-                                    var element = elementInfo.PinnedElement;
-                                    UpdateElementIndex(element, virtInfo, dataIndex + newCount);
-                                }
-                            }
-                        }
-                        break;
-                    }
-
-                case NotifyCollectionChangedAction.Replace:
-                    {
-                        // Requirement: oldStartIndex == newStartIndex. It is not a replace if this is not true.
-                        // Two cases here
-                        // case 1: oldCount == newCount 
-                        //         indices are not affected. nothing to do here.  
-                        // case 2: oldCount != newCount
-                        //         Replaced with less or more items. This is like an insert or remove
-                        //         depending on the counts.
-                        var oldStartIndex = args.OldStartingIndex;
-                        var newStartingIndex = args.NewStartingIndex;
-                        var oldCount = args.OldItems!.Count;
-                        var newCount = args.NewItems!.Count;
-                        if (oldStartIndex != newStartingIndex)
-                        {
-                            throw new NotSupportedException("Replace is only allowed with OldStartingIndex equals to NewStartingIndex.");
-                        }
-
-                        if (oldCount == 0)
-                        {
-                            throw new NotSupportedException("Replace notification with args.OldItemsCount value of 0 is not allowed. Use Insert action instead.");
-                        }
-
-                        if (newCount == 0)
-                        {
-                            throw new NotSupportedException("Replace notification with args.NewItemCount value of 0 is not allowed. Use Remove action instead.");
-                        }
-
-                        int countChange = newCount - oldCount;
-                        if (countChange != 0)
-                        {
-                            // countChange > 0 : countChange items were added
-                            // countChange < 0 : -countChange  items were removed
-                            foreach (var element in _owner.Children)
-                            {
-                                var virtInfo = ItemsRepeater.GetVirtualizationInfo(element);
-                                var dataIndex = virtInfo.Index;
-
-                                if (virtInfo.IsRealized)
-                                {
-                                    if (dataIndex >= oldStartIndex + oldCount)
-                                    {
-                                        UpdateElementIndex(element, virtInfo, dataIndex + countChange);
-                                    }
-                                }
-                            }
-
-                            EnsureFirstLastRealizedIndices();
-                            _lastRealizedElementIndexHeldByLayout += countChange;
-                        }
-                        break;
-                    }
-
-                case NotifyCollectionChangedAction.Remove:
-                    {
-                        var oldStartIndex = args.OldStartingIndex;
-                        var oldCount = args.OldItems!.Count;
-                        foreach (var element in _owner.Children)
-                        {
-                            var virtInfo = ItemsRepeater.GetVirtualizationInfo(element);
-                            var dataIndex = virtInfo.Index;
-
-                            if (virtInfo.IsRealized)
-                            {
-                                if (virtInfo.AutoRecycleCandidate && oldStartIndex <= dataIndex && dataIndex < oldStartIndex + oldCount)
-                                {
-                                    // If we are doing the mapping, remove the element who's data was removed.
-                                    _owner.ClearElementImpl(element);
-                                }
-                                else if (dataIndex >= (oldStartIndex + oldCount))
-                                {
-                                    UpdateElementIndex(element, virtInfo, dataIndex - oldCount);
-                                }
-                            }
-                        }
-
-                        InvalidateRealizedIndicesHeldByLayout();
-                        break;
-                    }
-
-                case NotifyCollectionChangedAction.Reset:
-                    // If we get multiple resets back to back before
-                    // running layout, we dont have to clear all the elements again.
-                    if (!_isDataSourceStableResetPending)
-                    {
-                        if (_owner.ItemsSourceView!.HasKeyIndexMapping)
-                        {
-                            _isDataSourceStableResetPending = true;
-                        }
-
-                        // Walk through all the elements and make sure they are cleared, they will go into
-                        // the stable id reset pool.
-                        foreach (var element in _owner.Children)
-                        {
-                            var virtInfo = ItemsRepeater.GetVirtualizationInfo(element);
-                            if (virtInfo.IsRealized && virtInfo.AutoRecycleCandidate)
-                            {
-                                _owner.ClearElementImpl(element);
-                            }
-                        }
-                    }
-
-                    InvalidateRealizedIndicesHeldByLayout();
-                    break;
-            }
-        }
-
-        private void EnsureFirstLastRealizedIndices()
-        {
-            if (_firstRealizedElementIndexHeldByLayout == FirstRealizedElementIndexDefault)
-            {
-                // This will ensure that the indexes are updated.
-                GetElementIfAlreadyHeldByLayout(0);
-            }
-        }
-
-        public void OnLayoutChanging()
-        {
-            if (_owner.ItemsSourceView?.HasKeyIndexMapping == true)
-            {
-                _isDataSourceStableResetPending = true;
-            }
-        }
-
-        public void OnOwnerArranged()
-        {
-            if (_isDataSourceStableResetPending)
-            {
-                _isDataSourceStableResetPending = false;
-
-                foreach (var entry in _resetPool)
-                {
-                    // TODO: Task 14204306: ItemsRepeater: Find better focus candidate when focused element is deleted in the ItemsSource.
-                    // Focused element is getting cleared. Need to figure out semantics on where
-                    // focus should go when the focused element is removed from the data collection.
-                    ClearElement(entry.Value, true /* isClearedDueToCollectionChange */);
-                }
-
-                _resetPool.Clear();
-
-                // Flush the realized indices once the stable reset pool is cleared to start fresh.
-                InvalidateRealizedIndicesHeldByLayout();
-            }
-        }
-
-        // We optimize for the case where index is not realized to return null as quickly as we can.
-        // Flow layouts manage containers on their own and will never ask for an index that is already realized.
-        // If an index that is realized is requested by the layout, we unfortunately have to walk the
-        // children. Not ideal, but a reasonable default to provide consistent behavior between virtualizing
-        // and non-virtualizing hosts.
-        private Control? GetElementIfAlreadyHeldByLayout(int index)
-        {
-            Control? element = null;
-
-            bool cachedFirstLastIndicesInvalid = _firstRealizedElementIndexHeldByLayout == FirstRealizedElementIndexDefault;
-            bool isRequestedIndexInRealizedRange = (_firstRealizedElementIndexHeldByLayout <= index && index <= _lastRealizedElementIndexHeldByLayout);
-
-            if (cachedFirstLastIndicesInvalid || isRequestedIndexInRealizedRange)
-            {
-                foreach (var child in _owner.Children)
-                {
-                    var virtInfo = ItemsRepeater.TryGetVirtualizationInfo(child);
-                    if (virtInfo?.IsHeldByLayout == true)
-                    {
-                        // Only give back elements held by layout. If someone else is holding it, they will be served by other methods.
-                        int childIndex = virtInfo.Index;
-                        _firstRealizedElementIndexHeldByLayout = Math.Min(_firstRealizedElementIndexHeldByLayout, childIndex);
-                        _lastRealizedElementIndexHeldByLayout = Math.Max(_lastRealizedElementIndexHeldByLayout, childIndex);
-                        if (virtInfo.Index == index)
-                        {
-                            element = child;
-                            // If we have valid first/last indices, we don't have to walk the rest, but if we 
-                            // do not, then we keep walking through the entire children collection to get accurate
-                            // indices once.
-                            if (!cachedFirstLastIndicesInvalid)
-                            {
-                                break;
-                            }
-                        }
-                    }
-                }
-            }
-
-            return element;
-        }
-
-        private Control? GetElementFromUniqueIdResetPool(int index)
-        {
-            Control? element = null;
-            // See if you can get it from the reset pool.
-            if (_isDataSourceStableResetPending)
-            {
-                element = _resetPool.Remove(index);
-                if (element != null)
-                {
-                    // Make sure that the index is updated to the current one
-                    var virtInfo = ItemsRepeater.GetVirtualizationInfo(element);
-                    virtInfo.MoveOwnershipToLayoutFromUniqueIdResetPool();
-                    UpdateElementIndex(element, virtInfo, index);
-
-                    // Update realized indices
-                    _firstRealizedElementIndexHeldByLayout = Math.Min(_firstRealizedElementIndexHeldByLayout, index);
-                    _lastRealizedElementIndexHeldByLayout = Math.Max(_lastRealizedElementIndexHeldByLayout, index);
-                }
-            }
-
-            return element;
-        }
-
-        private Control? GetElementFromPinnedElements(int index)
-        {
-            Control? element = null;
-
-            // See if you can find something among the pinned elements.
-            for (var i = 0; i < _pinnedPool.Count; ++i)
-            {
-                var elementInfo = _pinnedPool[i];
-                var virtInfo = elementInfo.VirtualizationInfo;
-
-                if (virtInfo.Index == index)
-                {
-                    _pinnedPool.RemoveAt(i);
-                    element = elementInfo.PinnedElement;
-                    elementInfo.VirtualizationInfo.MoveOwnershipToLayoutFromPinnedPool();
-
-                    // Update realized indices
-                    _firstRealizedElementIndexHeldByLayout = Math.Min(_firstRealizedElementIndexHeldByLayout, index);
-                    _lastRealizedElementIndexHeldByLayout = Math.Max(_lastRealizedElementIndexHeldByLayout, index);
-                    break;
-                }
-            }
-
-            return element;
-        }
-
-        // There are several cases handled here with respect to which element gets returned and when DataContext is modified.
-        //
-        // 1. If there is no ItemTemplate:
-        //    1.1 If data is an Control -> the data is returned
-        //    1.2 If data is not an Control -> a default DataTemplate is used to fetch element and DataContext is set to data
-        //
-        // 2. If there is an ItemTemplate:
-        //    2.1 If data is not an Control -> Element is fetched from ElementFactory and DataContext is set to the data
-        //    2.2 If data is an Control:
-        //        2.2.1 If Element returned by the ElementFactory is the same as the data -> Element (a.k.a. data) is returned as is
-        //        2.2.2 If Element returned by the ElementFactory is not the same as the data
-        //                 -> Element that is fetched from the ElementFactory is returned and
-        //                    DataContext is set to the data's DataContext (if it exists), otherwise it is set to the data itself
-        private Control GetElementFromElementFactory(int index)
-        {
-            // The view generator is the provider of last resort.
-            var data = _owner.ItemsSourceView!.GetAt(index);
-            var providedElementFactory = _owner.ItemTemplateShim;
-
-            IElementFactory GetElementFactory()
-            {
-                if (providedElementFactory == null)
-                {
-                    var factory = FuncDataTemplate.Default;
-                    _owner.ItemTemplate = factory;
-                    return _owner.ItemTemplateShim!;
-                }
-
-                return providedElementFactory;
-            }
-
-            Control GetElement()
-            {
-                if (providedElementFactory == null)
-                {
-                    if (data is Control dataAsElement)
-                    {
-                        return dataAsElement;
-                    }
-                }
-
-                var elementFactory = GetElementFactory();
-                var args = _elementFactoryGetArgs ??= new ElementFactoryGetArgs();
-
-                try
-                {
-                    args.Data = data;
-                    args.Parent = _owner;
-                    args.Index = index;
-                    return elementFactory.GetElement(args);
-                }
-                finally
-                {
-                    args.Data = null;
-                    args.Parent = null;
-                }
-            }
-
-            var element = GetElement();
-
-            var virtInfo = ItemsRepeater.GetVirtualizationInfo(element);
-            // Clear flag
-            virtInfo.MustClearDataContext = false;
-
-            if (data != element)
-            {
-                // Prepare the element
-                element.DataContext = data;
-                virtInfo.MustClearDataContext = true;
-            }
-
-            virtInfo.MoveOwnershipToLayoutFromElementFactory(
-                index,
-                /* uniqueId: */
-                _owner.ItemsSourceView.HasKeyIndexMapping ?
-                _owner.ItemsSourceView.KeyFromIndex(index) :
-                string.Empty);
-
-            // The view generator is the only provider that prepares the element.
-            var repeater = _owner;
-
-            // Add the element to the children collection here before raising OnElementPrepared so 
-            // that handlers can walk up the tree in case they want to find their IndexPath in the 
-            // nested case.
-            var children = repeater.Children;
-            if (element.GetVisualParent() != repeater)
-            {
-                children.Add(element);
-            }
-
-            repeater.OnElementPrepared(element, virtInfo);
-
-            // Update realized indices
-            _firstRealizedElementIndexHeldByLayout = Math.Min(_firstRealizedElementIndexHeldByLayout, index);
-            _lastRealizedElementIndexHeldByLayout = Math.Max(_lastRealizedElementIndexHeldByLayout, index);
-
-            return element;
-        }
-
-        private bool ClearElementToUniqueIdResetPool(Control element, VirtualizationInfo virtInfo)
-        {
-            if (_isDataSourceStableResetPending)
-            {
-                _resetPool.Add(element);
-                virtInfo.MoveOwnershipToUniqueIdResetPoolFromLayout();
-            }
-
-            return _isDataSourceStableResetPending;
-        }
-
-        private bool ClearElementToPinnedPool(Control element, VirtualizationInfo virtInfo, bool isClearedDueToCollectionChange)
-        {
-            bool moveToPinnedPool =
-                !isClearedDueToCollectionChange && virtInfo.IsPinned;
-
-            if (moveToPinnedPool)
-            {
-                _pinnedPool.Add(new PinnedElementInfo(element));
-                virtInfo.MoveOwnershipToPinnedPool();
-            }
-
-            return moveToPinnedPool;
-        }
-
-        private void UpdateFocusedElement()
-        {
-            Control? focusedElement = null;
-
-            if (TopLevel.GetTopLevel(_owner)?.FocusManager?.GetFocusedElement() is Visual child)
-            {
-                var parent = child.GetVisualParent();
-                var owner = _owner;
-
-                // Find out if the focused element belongs to one of our direct
-                // children.
-                while (parent != null)
-                {
-                    if (parent is ItemsRepeater repeater)
-                    {
-                        if (repeater == owner &&
-                            child is Control element &&
-                            ItemsRepeater.GetVirtualizationInfo(element).IsRealized)
-                        {
-                            focusedElement = element;
-                        }
-
-                        break;
-                    }
-
-                    child = parent;
-                    parent = child.GetVisualParent();
-                }
-            }
-
-            // If the focused element has changed,
-            // we need to unpin the old one and pin the new one.
-            if (_lastFocusedElement != focusedElement)
-            {
-                if (_lastFocusedElement != null)
-                {
-                    UpdatePin(_lastFocusedElement, false /* addPin */);
-                }
-
-                if (focusedElement != null)
-                {
-                    UpdatePin(focusedElement, true /* addPin */);
-                }
-
-                _lastFocusedElement = focusedElement;
-            }
-        }
-
-        private void OnFocusChanged(object? sender, RoutedEventArgs e) => UpdateFocusedElement();
-
-        private void EnsureEventSubscriptions()
-        {
-            if (!_eventsSubscribed)
-            {
-                _owner.GotFocus += OnFocusChanged;
-                _owner.LostFocus += OnFocusChanged;
-                _eventsSubscribed = true;
-            }
-        }
-
-        private void UpdateElementIndex(Control element, VirtualizationInfo virtInfo, int index)
-        {
-            var oldIndex = virtInfo.Index;
-            if (oldIndex != index)
-            {
-                virtInfo.UpdateIndex(index);
-                _owner.OnElementIndexChanged(element, oldIndex, index);
-            }
-        }
-
-        private void InvalidateRealizedIndicesHeldByLayout()
-        {
-            _firstRealizedElementIndexHeldByLayout = FirstRealizedElementIndexDefault;
-            _lastRealizedElementIndexHeldByLayout = LastRealizedElementIndexDefault;
-        }
-
-        private struct PinnedElementInfo
-        {
-            public PinnedElementInfo(Control element)
-            {
-                PinnedElement = element;
-                VirtualizationInfo = ItemsRepeater.GetVirtualizationInfo(element);
-            }
-
-            public Control PinnedElement { get; }
-            public VirtualizationInfo VirtualizationInfo { get; }
-        }
-    }
-}

+ 0 - 550
src/Avalonia.Controls.ItemsRepeater/Controls/ViewportManager.cs

@@ -1,550 +0,0 @@
-// This source file is adapted from the WinUI project.
-// (https://github.com/microsoft/microsoft-ui-xaml)
-//
-// Licensed to The Avalonia Project under MIT License, courtesy of The .NET Foundation.
-
-using System;
-using Avalonia.Layout;
-using Avalonia.Logging;
-using Avalonia.Threading;
-using Avalonia.VisualTree;
-
-namespace Avalonia.Controls
-{
-    internal class ViewportManager
-    {
-        private const double CacheBufferPerSideInflationPixelDelta = 40.0;
-        private readonly ItemsRepeater _owner;
-        private bool _ensuredScroller;
-        private IScrollAnchorProvider? _scroller;
-        private Control? _makeAnchorElement;
-        private bool _isAnchorOutsideRealizedRange;
-        private Rect _visibleWindow;
-        private Rect _layoutExtent;
-        // This is the expected shift by the layout.
-        private Point _expectedViewportShift;
-        // This is what is pending and not been accounted for. 
-        // Sometimes the scrolling surface cannot service a shift (for example
-        // it is already at the top and cannot shift anymore.)
-        private Point _pendingViewportShift;
-        // Unshiftable shift amount that this view manager can
-        // handle on its own to fake it to the layout as if the shift
-        // actually happened. This can happen in cases where no scrollviewer
-        // in the parent chain can scroll in the shift direction.
-        private Point _unshiftableShift;
-        private double _maximumHorizontalCacheLength = 2.0;
-        private double _maximumVerticalCacheLength = 2.0;
-        private double _horizontalCacheBufferPerSide;
-        private double _verticalCacheBufferPerSide;
-        private bool _isBringIntoViewInProgress;
-        // For non-virtualizing layouts, we do not need to keep
-        // updating viewports and invalidating measure often. So when
-        // a non virtualizing layout is used, we stop doing all that work.
-        private bool _managingViewportDisabled;
-        private bool _effectiveViewportChangedSubscribed;
-        private bool _layoutUpdatedSubscribed;
-
-        public ViewportManager(ItemsRepeater owner)
-        {
-            _owner = owner;
-        }
-
-        public Control? SuggestedAnchor
-        {
-            get
-            {
-                // The element generated during the ItemsRepeater.MakeAnchor call has precedence over the next tick.
-                var suggestedAnchor = _makeAnchorElement;
-                var owner = _owner;
-
-                if (suggestedAnchor == null)
-                {
-                    var anchorElement = _scroller?.CurrentAnchor;
-
-                    if (anchorElement != null)
-                    {
-                        // We can't simply return anchorElement because, in case of nested Repeaters, it may not
-                        // be a direct child of ours, or even an indirect child. We need to walk up the tree starting
-                        // from anchorElement to figure out what child of ours (if any) to use as the suggested element.
-                        var child = anchorElement;
-                        var parent = child.GetVisualParent() as Control;
-
-                        while (parent != null)
-                        {
-                            if (parent == owner)
-                            {
-                                suggestedAnchor = child;
-                                break;
-                            }
-
-                            child = parent;
-                            parent = parent.GetVisualParent() as Control;
-                        }
-                    }
-                }
-
-                return suggestedAnchor;
-            }
-        }
-
-        public bool HasScroller => _scroller != null;
-
-        public Control? MadeAnchor => _makeAnchorElement;
-
-        public double HorizontalCacheLength
-        {
-            get => _maximumHorizontalCacheLength;
-            set
-            {
-                if (_maximumHorizontalCacheLength != value)
-                {
-                    ValidateCacheLength(value);
-                    _maximumHorizontalCacheLength = value;
-                }
-            }
-        }
-
-        public double VerticalCacheLength
-        {
-            get => _maximumVerticalCacheLength;
-            set
-            {
-                if (_maximumVerticalCacheLength != value)
-                {
-                    ValidateCacheLength(value);
-                    _maximumVerticalCacheLength = value;
-                }
-            }
-        }
-
-        public Rect GetLayoutVisibleWindow()
-        {
-            var visibleWindow = _visibleWindow;
-
-            if (_makeAnchorElement != null)
-            {
-                // The anchor is not necessarily laid out yet. Its position should default
-                // to zero and the layout origin is expected to change once layout is done.
-                // Until then, we need a window that's going to protect the anchor from
-                // getting recycled.
-                visibleWindow = visibleWindow.WithX(0).WithY(0);
-            }
-            else if (HasScroller)
-            {
-                visibleWindow = new Rect(
-                    visibleWindow.X + _layoutExtent.X + _expectedViewportShift.X + _unshiftableShift.X,
-                    visibleWindow.Y + _layoutExtent.Y + _expectedViewportShift.Y + _unshiftableShift.Y,
-                    visibleWindow.Width,
-                    visibleWindow.Height);
-            }
-
-            return visibleWindow;
-        }
-
-        public Rect GetLayoutRealizationWindow()
-        {
-            var realizationWindow = GetLayoutVisibleWindow();
-            if (HasScroller)
-            {
-                realizationWindow = new Rect(
-                    realizationWindow.X - _horizontalCacheBufferPerSide,
-                    realizationWindow.Y - _verticalCacheBufferPerSide,
-                    realizationWindow.Width + _horizontalCacheBufferPerSide * 2.0,
-                    realizationWindow.Height + _verticalCacheBufferPerSide * 2.0);
-            }
-
-            return realizationWindow;
-        }
-
-        public void SetLayoutExtent(Rect extent)
-        {
-            _expectedViewportShift = new Point(
-                _expectedViewportShift.X + _layoutExtent.X - extent.X,
-                _expectedViewportShift.Y + _layoutExtent.Y - extent.Y);
-
-            // We tolerate viewport imprecisions up to 1 pixel to avoid invalidating layout too much.
-            if (Math.Abs(_expectedViewportShift.X) > 1 || Math.Abs(_expectedViewportShift.Y) > 1)
-            {
-                Logger.TryGet(LogEventLevel.Verbose, "Repeater")?.Log(this, "{LayoutId}: Expecting viewport shift of ({Shift})",
-                    _owner.Layout?.LayoutId, _expectedViewportShift);
-
-                // There are cases where we might be expecting a shift but not get it. We will
-                // be waiting for the effective viewport event but if the scroll viewer is not able
-                // to perform the shift (perhaps because it cannot scroll in negative offset),
-                // then we will end up not realizing elements in the visible 
-                // window. To avoid this, we register to layout updated for this layout pass. If we 
-                // get an effective viewport, we know we have a new viewport and we unregister from
-                // layout updated. If we get the layout updated handler, then we know that the 
-                // scroller was unable to perform the shift and we invalidate measure and unregister
-                // from the layout updated event.
-                if (!_layoutUpdatedSubscribed)
-                {
-                    _owner.LayoutUpdated += OnLayoutUpdated;
-                    _layoutUpdatedSubscribed = true;
-                }
-            }
-
-            _layoutExtent = extent;
-            _pendingViewportShift = _expectedViewportShift;
-
-            // We just finished a measure pass and have a new extent.
-            // Let's make sure the scrollers will run its arrange so that they track the anchor.
-            ((Control?)_scroller)?.InvalidateArrange();
-        }
-
-        public Point GetOrigin() => _layoutExtent.TopLeft;
-
-        public void OnLayoutChanged(bool isVirtualizing)
-        {
-            _managingViewportDisabled = !isVirtualizing;
-
-            _layoutExtent = default;
-            _expectedViewportShift = default;
-            _pendingViewportShift = default;
-            _unshiftableShift = default;
-
-            if (_managingViewportDisabled && _effectiveViewportChangedSubscribed)
-            {
-                _owner.EffectiveViewportChanged -= OnEffectiveViewportChanged;
-                _effectiveViewportChangedSubscribed = false;
-            }
-            else if (!_managingViewportDisabled && !_effectiveViewportChangedSubscribed)
-            {
-                _owner.EffectiveViewportChanged += OnEffectiveViewportChanged;
-                _effectiveViewportChangedSubscribed = true;
-            }
-        }
-
-        public void OnElementPrepared(Control element, VirtualizationInfo virtInfo)
-        {
-            // WinUI registers the element as an anchor candidate here, but I feel that's in error:
-            // at this point the element has not yet been positioned by the arrange pass so it will
-            // have its previous position, meaning that when the arrange pass moves it into its new
-            // position, an incorrect scroll anchoring will occur. Instead signal that it's not yet
-            // registered as a scroll anchor candidate.
-            virtInfo.IsRegisteredAsAnchorCandidate = false;
-        }
-
-        public void OnElementCleared(Control element, VirtualizationInfo virtInfo)
-        {
-            _scroller?.UnregisterAnchorCandidate(element);
-            virtInfo.IsRegisteredAsAnchorCandidate = false;
-        }
-
-        public void OnOwnerMeasuring()
-        {
-            // This is because of a bug that causes effective viewport to not
-            // fire if you register during arrange.
-            // Bug 17411076: EffectiveViewport: registering for effective viewport in arrange should invalidate viewport
-            EnsureScroller();
-        }
-
-        public void OnOwnerArranged()
-        {
-            _expectedViewportShift = default;
-
-            if (!_managingViewportDisabled)
-            {
-                // This is because of a bug that causes effective viewport to not 
-                // fire if you register during arrange.
-                // Bug 17411076: EffectiveViewport: registering for effective viewport in arrange should invalidate viewport
-                // EnsureScroller();
-
-                if (HasScroller)
-                {
-                    double maximumHorizontalCacheBufferPerSide = _maximumHorizontalCacheLength * _visibleWindow.Width / 2.0;
-                    double maximumVerticalCacheBufferPerSide = _maximumVerticalCacheLength * _visibleWindow.Height / 2.0;
-
-                    bool continueBuildingCache =
-                        _horizontalCacheBufferPerSide < maximumHorizontalCacheBufferPerSide ||
-                        _verticalCacheBufferPerSide < maximumVerticalCacheBufferPerSide;
-
-                    if (continueBuildingCache)
-                    {
-                        _horizontalCacheBufferPerSide += CacheBufferPerSideInflationPixelDelta;
-                        _verticalCacheBufferPerSide += CacheBufferPerSideInflationPixelDelta;
-
-                        _horizontalCacheBufferPerSide = Math.Min(_horizontalCacheBufferPerSide, maximumHorizontalCacheBufferPerSide);
-                        _verticalCacheBufferPerSide = Math.Min(_verticalCacheBufferPerSide, maximumVerticalCacheBufferPerSide);
-                    }
-                }
-            }
-        }
-
-        private void OnLayoutUpdated(object? sender, EventArgs args)
-        {
-            _owner.LayoutUpdated -= OnLayoutUpdated;
-            _layoutUpdatedSubscribed = false;
-            if (_managingViewportDisabled)
-            {
-                return;
-            }
-
-            // We were expecting a viewport shift but we never got one and we are not going to in this
-            // layout pass. We likely will never get this shift, so lets assume that we are never going to get it and
-            // adjust our expected shift to track that. One case where this can happen is when there is no scrollviewer
-            // that can scroll in the direction where the shift is expected.
-            if (_pendingViewportShift.X != 0 || _pendingViewportShift.Y != 0)
-            {
-                Logger.TryGet(LogEventLevel.Verbose, "Repeater")?.Log(this, "{LayoutId}: Layout Updated with pending shift {Shift}- invalidating measure",
-                    _owner.Layout?.LayoutId,
-                    _pendingViewportShift);
-
-                // Assume this is never going to come.
-                _unshiftableShift = new Point(
-                    _unshiftableShift.X + _pendingViewportShift.X,
-                    _unshiftableShift.Y + _pendingViewportShift.Y);
-                _pendingViewportShift = default;
-                _expectedViewportShift = default;
-
-                TryInvalidateMeasure();
-            }
-        }
-
-        public void OnMakeAnchor(Control? anchor, bool isAnchorOutsideRealizedRange)
-        {
-            if (_makeAnchorElement != anchor)
-            {
-                _makeAnchorElement = anchor;
-                _isAnchorOutsideRealizedRange = isAnchorOutsideRealizedRange;
-            }
-        }
-
-        public void OnBringIntoViewRequested(RequestBringIntoViewEventArgs args)
-        {
-            if (!_managingViewportDisabled)
-            {
-                // During the time between a bring into view request and the element coming into view we do not
-                // want the anchor provider to pick some anchor and jump to it. Instead we want to anchor on the
-                // element that is being brought into view. We can do this by making just that element as a potential
-                // anchor candidate and ensure no other element of this repeater is an anchor candidate.
-                // Once the layout pass is done and we render the frame, the element will be in frame and we can
-                // switch back to letting the anchor provider pick a suitable anchor.
-
-                // get the targetChild - i.e the immediate child of this repeater that is being brought into view.
-                // Note that the element being brought into view could be a descendant.
-                var targetChild = GetImmediateChildOfRepeater((Control)args.TargetObject!);
-
-                if (targetChild is null)
-                {
-                    return;
-                }
-
-                // Make sure that only the target child can be the anchor during the bring into view operation.
-                if (_scroller is object)
-                {
-                    foreach (var child in _owner.Children)
-                    {
-                        var info = ItemsRepeater.GetVirtualizationInfo(child);
-
-                        if (child != targetChild && info.IsRegisteredAsAnchorCandidate)
-                        {
-                            _scroller.UnregisterAnchorCandidate(child);
-                            info.IsRegisteredAsAnchorCandidate = false;
-                        }
-                    }
-                }
-
-                // Register action to go back to how things were before where any child can be the anchor. Here,
-                // WinUI uses CompositionTarget.Rendering but we don't currently have that, so post an action to
-                // run *after* rendering has completed (priority needs to be lower than Render as Transformed
-                // bounds must have been set in order for OnEffectiveViewportChanged to trigger).
-                if (!_isBringIntoViewInProgress)
-                {
-                    _isBringIntoViewInProgress = true;
-                    Dispatcher.UIThread.Post(OnCompositionTargetRendering, DispatcherPriority.Loaded);
-                }
-            }
-        }
-
-        public void RegisterScrollAnchorCandidate(Control element, VirtualizationInfo virtInfo)
-        {
-            if (!virtInfo.IsRegisteredAsAnchorCandidate)
-            {
-                _scroller?.RegisterAnchorCandidate(element);
-                virtInfo.IsRegisteredAsAnchorCandidate = true;
-            }
-        }
-
-        private Control? GetImmediateChildOfRepeater(Control descendant)
-        {
-            var targetChild = descendant;
-            var parent = (Control?)descendant.GetVisualParent();
-            while (parent != null && parent != _owner)
-            {
-                targetChild = parent;
-                parent = (Control?)parent.GetVisualParent();
-            }
-
-            if (parent == null)
-            {
-                return null;
-            }
-
-            return targetChild;
-        }
-
-        private void OnCompositionTargetRendering()
-        {
-            _isBringIntoViewInProgress = false;
-            _makeAnchorElement = null;
-
-            // Undo the anchor deregistrations done by OnBringIntoViewRequested.
-            if (_scroller is object)
-            {
-                foreach (var child in _owner.Children)
-                {
-                    var info = ItemsRepeater.GetVirtualizationInfo(child);
-
-                    // The item brought into view is still registered - don't register it more than once.
-                    if (info.IsRealized && info.IsHeldByLayout && !info.IsRegisteredAsAnchorCandidate)
-                    {
-                        _scroller.RegisterAnchorCandidate(child);
-                        info.IsRegisteredAsAnchorCandidate = true;
-                    }
-                }
-            }
-
-            // HACK: Invalidate measure now that the anchor has been removed so that a layout can be
-            // done with a proper realization rect. This is a hack not present upstream to try to fix
-            // https://github.com/microsoft/microsoft-ui-xaml/issues/1422
-            TryInvalidateMeasure();
-        }
-
-        public void ResetScrollers()
-        {
-            if (_scroller is object)
-            {
-                foreach (var child in _owner.Children)
-                {
-                    var info = ItemsRepeater.GetVirtualizationInfo(child);
-
-                    if (info.IsRegisteredAsAnchorCandidate)
-                    {
-                        _scroller.UnregisterAnchorCandidate(child);
-                        info.IsRegisteredAsAnchorCandidate = false;
-                    }
-                }
-
-                _scroller = null;
-            }
-
-            _owner.EffectiveViewportChanged -= OnEffectiveViewportChanged;
-            _effectiveViewportChangedSubscribed = false;
-            _ensuredScroller = false;
-        }
-
-        private void OnEffectiveViewportChanged(object? sender, EffectiveViewportChangedEventArgs e)
-        {
-            Logger.TryGet(LogEventLevel.Verbose, "Repeater")?.Log(this, "{LayoutId}: EffectiveViewportChanged event callback", _owner.Layout?.LayoutId);
-            UpdateViewport(e.EffectiveViewport);
-
-            _pendingViewportShift = default;
-            _unshiftableShift = default;
-            if (_visibleWindow.Width == 0 && _visibleWindow.Height == 0)
-            {
-                // We got cleared.
-                _layoutExtent = default;
-            }
-
-            // We got a new viewport, we dont need to wait for layout updated anymore to 
-            // see if our request for a pending shift was handled.
-            if (_layoutUpdatedSubscribed)
-            {
-                _owner.LayoutUpdated -= OnLayoutUpdated;
-                _layoutUpdatedSubscribed = false;
-            }
-        }
-
-        private void EnsureScroller()
-        {
-            if (!_ensuredScroller)
-            {
-                ResetScrollers();
-
-                var parent = _owner.GetVisualParent();
-                while (parent != null)
-                {
-                    if (parent is IScrollAnchorProvider scroller)
-                    {
-                        _scroller = scroller;
-                        break;
-                    }
-
-                    parent = parent.GetVisualParent();
-                }
-
-                if (!_managingViewportDisabled)
-                {
-                    _owner.EffectiveViewportChanged += OnEffectiveViewportChanged;
-                    _effectiveViewportChangedSubscribed = true;
-                }
-
-                _ensuredScroller = true;
-            }
-        }
-
-        private void UpdateViewport(Rect viewport)
-        {
-            var currentVisibleWindow = viewport;
-            var previousVisibleWindow = _visibleWindow;
-
-            Logger.TryGet(LogEventLevel.Verbose, "Repeater")?.Log(this, "{LayoutId}: Effective Viewport: ({Before})->({After})",
-                _owner.Layout?.LayoutId,
-                previousVisibleWindow,
-                viewport);
-
-            if (-currentVisibleWindow.X <= ItemsRepeater.ClearedElementsArrangePosition.X &&
-                -currentVisibleWindow.Y <= ItemsRepeater.ClearedElementsArrangePosition.Y)
-            {
-                Logger.TryGet(LogEventLevel.Verbose, "Repeater")?.Log(this, "{LayoutId}: Viewport is invalid. visible window cleared", _owner.Layout?.LayoutId);
-                // We got cleared.
-                _visibleWindow = default;
-            }
-            else
-            {
-                _visibleWindow = currentVisibleWindow;
-            }
-
-            if (_visibleWindow != previousVisibleWindow)
-            {
-                Logger.TryGet(LogEventLevel.Verbose, "Repeater")?.Log(this, "{LayoutId}: Used Viewport: ({Before})->({After})",
-                    _owner.Layout?.LayoutId,
-                    previousVisibleWindow,
-                    currentVisibleWindow);
-                TryInvalidateMeasure();
-            }
-        }
-
-        private static void ValidateCacheLength(double cacheLength)
-        {
-            if (cacheLength < 0.0 || double.IsInfinity(cacheLength) || double.IsNaN(cacheLength))
-            {
-                throw new ArgumentException("The maximum cache length must be equal or superior to zero.");
-            }
-        }
-
-        private void TryInvalidateMeasure()
-        {
-            // Don't invalidate measure if we have an invalid window.
-            if (_visibleWindow.Width != 0 || _visibleWindow.Height != 0)
-            {
-                // We invalidate measure instead of just invalidating arrange because
-                // we don't invalidate measure in UpdateViewport if the view is changing to
-                // avoid layout cycles.
-                Logger.TryGet(LogEventLevel.Verbose, "Repeater")?.Log(this, "{LayoutId}: Invalidating measure due to viewport change", _owner.Layout?.LayoutId);
-                _owner.InvalidateMeasure();
-            }
-        }
-
-        private class ScrollerInfo
-        {
-            public ScrollerInfo(ScrollViewer scroller)
-            {
-                Scroller = scroller;
-            }
-
-            public ScrollViewer Scroller { get; }
-        }
-    };
-}

+ 0 - 119
src/Avalonia.Controls.ItemsRepeater/Controls/VirtualizationInfo.cs

@@ -1,119 +0,0 @@
-// This source file is adapted from the WinUI project.
-// (https://github.com/microsoft/microsoft-ui-xaml)
-//
-// Licensed to The Avalonia Project under MIT License, courtesy of The .NET Foundation.
-
-using System;
-
-namespace Avalonia.Controls
-{
-    internal enum ElementOwner
-    {
-        // All elements are originally owned by the view generator.
-        ElementFactory,
-        // Ownership is transferred to the layout when it calls GetElement.
-        Layout,
-        // Ownership is transferred to the pinned pool if the element is cleared (outside of
-        // a 'remove' collection change of course).
-        PinnedPool,
-        // Ownership is transfered to the reset pool if the element is cleared by a reset and
-        // the data source supports unique ids.
-        UniqueIdResetPool,
-        // Ownership is transfered to the animator if the element is cleared due to a
-        // 'remove'-like collection change.
-        Animator
-    }
-
-    internal class VirtualizationInfo
-    {
-        private int _pinCounter;
-
-        public Rect ArrangeBounds { get; set; }
-        public bool AutoRecycleCandidate { get; set; }
-        public int Index { get; private set; }
-        public bool IsPinned => _pinCounter > 0;
-        public bool IsHeldByLayout => Owner == ElementOwner.Layout;
-        public bool IsRealized => IsHeldByLayout || Owner == ElementOwner.PinnedPool;
-        public bool IsInUniqueIdResetPool => Owner == ElementOwner.UniqueIdResetPool;
-        public bool MustClearDataContext { get; set; }
-        public bool KeepAlive { get; set; }
-        public bool IsRegisteredAsAnchorCandidate { get; set; }
-        public ElementOwner Owner { get; private set; } = ElementOwner.ElementFactory;
-        public string? UniqueId { get; private set; }
-
-        public void MoveOwnershipToLayoutFromElementFactory(int index, string uniqueId)
-        {
-            Owner = ElementOwner.Layout;
-            Index = index;
-            UniqueId = uniqueId;
-        }
-
-        public void MoveOwnershipToLayoutFromUniqueIdResetPool()
-        {
-            Owner = ElementOwner.Layout;
-        }
-
-        public void MoveOwnershipToLayoutFromPinnedPool()
-        {
-            Owner = ElementOwner.Layout;
-        }
-
-        public void MoveOwnershipToElementFactory()
-        {
-            Owner = ElementOwner.ElementFactory;
-            _pinCounter = 0;
-            Index = -1;
-            UniqueId = string.Empty;
-            ArrangeBounds = ItemsRepeater.InvalidRect;
-        }
-
-        public void MoveOwnershipToUniqueIdResetPoolFromLayout()
-        {
-            Owner = ElementOwner.UniqueIdResetPool;
-            // Keep the pinCounter the same. If the container survives the reset
-            // it can go on being pinned as if nothing happened.
-        }
-
-        public void MoveOwnershipToAnimator()
-        {
-            // During a unique id reset, some elements might get removed.
-            // Their ownership will go from the UniqueIdResetPool to the Animator.
-            // The common path though is for ownership to go from Layout to Animator.
-            Owner = ElementOwner.Animator;
-            Index = -1;
-            _pinCounter = 0;
-        }
-
-        public void MoveOwnershipToPinnedPool()
-        {
-            Owner = ElementOwner.PinnedPool;
-        }
-
-        public int AddPin()
-        {
-            if (!IsRealized)
-            {
-                throw new InvalidOperationException("You can't pin an unrealized element.");
-            }
-
-            return ++_pinCounter;
-        }
-
-        public int RemovePin()
-        {
-            if (!IsRealized)
-            {
-                throw new InvalidOperationException("You can't unpin an unrealized element.");
-            }
-
-            if (!IsPinned)
-            {
-                throw new InvalidOperationException("UnpinElement was called more often than PinElement.");
-            }
-
-            return --_pinCounter;
-        }
-
-        public void UpdateIndex(int newIndex) => Index = newIndex;
-    }
-}

+ 0 - 219
src/Avalonia.Controls.ItemsRepeater/Layout/AttachedLayout.cs

@@ -1,219 +0,0 @@
-// This source file is adapted from the WinUI project.
-// (https://github.com/microsoft/microsoft-ui-xaml)
-//
-// Licensed to The Avalonia Project under MIT License, courtesy of The .NET Foundation.
-
-using System;
-using Avalonia.Utilities;
-
-namespace Avalonia.Layout
-{
-    /// <summary>
-    /// Represents the base class for an object that sizes and arranges child elements for a host.
-    /// </summary>
-    public abstract class AttachedLayout : AvaloniaObject
-    {
-        public string? LayoutId { get; set; }
-
-        /// <summary>
-        /// Occurs when the measurement state (layout) has been invalidated.
-        /// </summary>
-        public event EventHandler? MeasureInvalidated;
-
-        /// <summary>
-        /// Occurs when the measurement state (layout) has been invalidated.
-        /// </summary>
-        public static readonly WeakEvent<AttachedLayout, EventArgs> MeasureInvalidatedWeakEvent =
-            WeakEvent.Register<AttachedLayout>(
-                (s, h) => s.MeasureInvalidated += h,
-                (s, h) => s.MeasureInvalidated -= h);
-
-        /// <summary>
-        /// Occurs when the arrange state (layout) has been invalidated.
-        /// </summary>
-        public event EventHandler? ArrangeInvalidated;
-        
-        /// <summary>
-        /// Occurs when the arrange state (layout) has been invalidated.
-        /// </summary>
-        public static readonly WeakEvent<AttachedLayout, EventArgs> ArrangeInvalidatedWeakEvent =
-            WeakEvent.Register<AttachedLayout>(
-                (s, h) => s.ArrangeInvalidated += h,
-                (s, h) => s.ArrangeInvalidated -= h);
-
-        /// <summary>
-        /// Initializes any per-container state the layout requires when it is attached to an
-        /// <see cref="Layoutable"/> container.
-        /// </summary>
-        /// <param name="context">
-        /// The context object that facilitates communication between the layout and its host
-        /// container.
-        /// </param>
-        /// <remarks>
-        /// Container elements that support attached layouts should call this method when a layout
-        /// instance is first assigned. The container is expected to give the attached layout
-        /// instance a way to store and retrieve any per-container state by way of the provided
-        /// context. It is also the responsibility of the container to not reuse the context, or
-        /// otherwise expose the state from one layout to another.
-        ///
-        /// When an attached layout is removed the container should release any reference to the
-        /// layout state it stored.
-        /// 
-        /// Override <see cref="NonVirtualizingLayout.InitializeForContextCore"/> or
-        /// <see cref="VirtualizingLayout.InitializeForContextCore"/> to provide the behavior for
-        /// this method in a derived class.
-        /// </remarks>
-        public void InitializeForContext(LayoutContext context)
-        {
-            if (this is VirtualizingLayout virtualizingLayout)
-            {
-                var virtualizingContext = GetVirtualizingLayoutContext(context);
-                virtualizingLayout.InitializeForContextCore(virtualizingContext);
-            }
-            else if (this is NonVirtualizingLayout nonVirtualizingLayout)
-            {
-                var nonVirtualizingContext = GetNonVirtualizingLayoutContext(context);
-                nonVirtualizingLayout.InitializeForContextCore(nonVirtualizingContext);
-            }
-            else
-            {
-                throw new NotSupportedException();
-            }
-        }
-
-        /// <summary>
-        /// Removes any state the layout previously stored on the ILayoutable container.
-        /// </summary>
-        /// <param name="context">
-        /// The context object that facilitates communication between the layout and its host
-        /// container.
-        /// </param>
-        public void UninitializeForContext(LayoutContext context)
-        {
-            if (this is VirtualizingLayout virtualizingLayout)
-            {
-                var virtualizingContext = GetVirtualizingLayoutContext(context);
-                virtualizingLayout.UninitializeForContextCore(virtualizingContext);
-            }
-            else if (this is NonVirtualizingLayout nonVirtualizingLayout)
-            {
-                var nonVirtualizingContext = GetNonVirtualizingLayoutContext(context);
-                nonVirtualizingLayout.UninitializeForContextCore(nonVirtualizingContext);
-            }
-            else
-            {
-                throw new NotSupportedException();
-            }
-        }
-
-        /// <summary>
-        /// Suggests a DesiredSize for a container element. A container element that supports
-        /// attached layouts should call this method from their own MeasureOverride implementations
-        /// to form a recursive layout update. The attached layout is expected to call the Measure
-        /// for each of the container’s ILayoutable children.
-        /// </summary>
-        /// <param name="context">
-        /// The context object that facilitates communication between the layout and its host
-        /// container.
-        /// </param>
-        /// <param name="availableSize">
-        /// The available space that a container can allocate to a child object. A child object can
-        /// request a larger space than what is available; the provided size might be accommodated
-        /// if scrolling or other resize behavior is possible in that particular container.
-        /// </param>
-        /// <returns></returns>
-        public Size Measure(LayoutContext context, Size availableSize)
-        {
-            if (this is VirtualizingLayout virtualizingLayout)
-            {
-                var virtualizingContext = GetVirtualizingLayoutContext(context);
-                return virtualizingLayout.MeasureOverride(virtualizingContext, availableSize);
-            }
-            else if (this is NonVirtualizingLayout nonVirtualizingLayout)
-            {
-                var nonVirtualizingContext = GetNonVirtualizingLayoutContext(context);
-                return nonVirtualizingLayout.MeasureOverride(nonVirtualizingContext, availableSize);
-            }
-            else
-            {
-                throw new NotSupportedException();
-            }
-        }
-
-        /// <summary>
-        /// Positions child elements and determines a size for a container UIElement. Container
-        /// elements that support attached layouts should call this method from their layout
-        /// override implementations to form a recursive layout update.
-        /// </summary>
-        /// <param name="context">
-        /// The context object that facilitates communication between the layout and its host
-        /// container.
-        /// </param>
-        /// <param name="finalSize">
-        /// The final size that the container computes for the child in layout.
-        /// </param>
-        /// <returns>The actual size that is used after the element is arranged in layout.</returns>
-        public Size Arrange(LayoutContext context, Size finalSize)
-        {
-            if (this is VirtualizingLayout virtualizingLayout)
-            {
-                var virtualizingContext = GetVirtualizingLayoutContext(context);
-                return virtualizingLayout.ArrangeOverride(virtualizingContext, finalSize);
-            }
-            else if (this is NonVirtualizingLayout nonVirtualizingLayout)
-            {
-                var nonVirtualizingContext = GetNonVirtualizingLayoutContext(context);
-                return nonVirtualizingLayout.ArrangeOverride(nonVirtualizingContext, finalSize);
-            }
-            else
-            {
-                throw new NotSupportedException();
-            }
-        }
-
-        /// <summary>
-        /// Invalidates the measurement state (layout) for all ILayoutable containers that reference
-        /// this layout.
-        /// </summary>
-        protected void InvalidateMeasure() => MeasureInvalidated?.Invoke(this, EventArgs.Empty);
-
-        /// <summary>
-        /// Invalidates the arrange state (layout) for all UIElement containers that reference this
-        /// layout. After the invalidation, the UIElement will have its layout updated, which
-        /// occurs asynchronously.
-        /// </summary>
-        protected void InvalidateArrange() => ArrangeInvalidated?.Invoke(this, EventArgs.Empty);
-
-        private static VirtualizingLayoutContext GetVirtualizingLayoutContext(LayoutContext context)
-        {
-            if (context is VirtualizingLayoutContext virtualizingContext)
-            {
-                return virtualizingContext;
-            }
-            else if (context is NonVirtualizingLayoutContext nonVirtualizingContext)
-            {
-                return nonVirtualizingContext.GetVirtualizingContextAdapter();
-            }
-            else
-            {
-                throw new NotSupportedException();
-            }
-        }
-
-        private NonVirtualizingLayoutContext GetNonVirtualizingLayoutContext(LayoutContext context)
-        {
-            if (context is NonVirtualizingLayoutContext nonVirtualizingContext)
-            {
-                return nonVirtualizingContext;
-            }
-            else if (context is VirtualizingLayoutContext virtualizingContext)
-            {
-                return virtualizingContext.GetNonVirtualizingContextAdapter();
-            }
-            else
-            {
-                throw new NotSupportedException();
-            }
-        }
-    }
-}

+ 0 - 463
src/Avalonia.Controls.ItemsRepeater/Layout/ElementManager.cs

@@ -1,463 +0,0 @@
-// This source file is adapted from the WinUI project.
-// (https://github.com/microsoft/microsoft-ui-xaml)
-//
-// Licensed to The Avalonia Project under MIT License, courtesy of The .NET Foundation.
-
-using System;
-using System.Collections.Generic;
-using System.Collections.Specialized;
-using Avalonia.Layout.Utils;
-using Avalonia.Logging;
-
-namespace Avalonia.Layout
-{
-    internal class ElementManager
-    {
-        private readonly List<Layoutable?> _realizedElements = new List<Layoutable?>();
-        private readonly List<Rect> _realizedElementLayoutBounds = new List<Rect>();
-        private int _firstRealizedDataIndex;
-        private VirtualizingLayoutContext? _context;
-
-        private bool IsVirtualizingContext
-        {
-            get
-            {
-                if (_context != null)
-                {
-                    var rect = _context.RealizationRect;
-                    bool hasInfiniteSize = double.IsInfinity(rect.Height) || double.IsInfinity(rect.Width);
-                    return !hasInfiniteSize;
-                }
-                return false;
-            }
-        }
-
-        public void SetContext(VirtualizingLayoutContext virtualContext) => _context = virtualContext;
-
-        public void OnBeginMeasure(ScrollOrientation orientation)
-        {
-            if (_context != null)
-            {
-                if (IsVirtualizingContext)
-                {
-                    // We proactively clear elements laid out outside of the realization
-                    // rect so that they are available for reuse during the current
-                    // measure pass.
-                    // This is useful during fast panning scenarios in which the realization
-                    // window is constantly changing and we want to reuse elements from
-                    // the end that's opposite to the panning direction.
-                    DiscardElementsOutsideWindow(_context.RealizationRect, orientation);
-                }
-                else
-                {
-                    // If we are initialized with a non-virtualizing context, make sure that
-                    // we have enough space to hold the bounds for all the elements.
-                    int count = _context.ItemCount;
-                    if (_realizedElementLayoutBounds.Count != count)
-                    {
-                        // Make sure there is enough space for the bounds.
-                        // Note: We could optimize when the count becomes smaller, but keeping
-                        // it always up to date is the simplest option for now.
-                        _realizedElementLayoutBounds.Resize(count, default);
-                    }
-                }
-            }
-        }
-
-        public int GetRealizedElementCount()
-        {
-            return IsVirtualizingContext ? _realizedElements.Count : _context!.ItemCount;
-        }
-
-        public Layoutable GetAt(int realizedIndex)
-        {
-            Layoutable? element;
-
-            if (IsVirtualizingContext)
-            {
-                element = _realizedElements[realizedIndex];
-
-                if (element == null)
-                {
-                    // Sentinel. Create the element now since we need it.
-                    int dataIndex = GetDataIndexFromRealizedRangeIndex(realizedIndex);
-                    Logger.TryGet(LogEventLevel.Verbose, "Repeater")?.Log(this, "Creating element for sentinal with data index {Index}", dataIndex);
-                    element = _context!.GetOrCreateElementAt(
-                        dataIndex,
-                        ElementRealizationOptions.ForceCreate | ElementRealizationOptions.SuppressAutoRecycle);
-                    _realizedElements[realizedIndex] = element;
-                }
-            }
-            else
-            {
-                // realizedIndex and dataIndex are the same (everything is realized)
-                element = _context!.GetOrCreateElementAt(
-                    realizedIndex,
-                    ElementRealizationOptions.ForceCreate | ElementRealizationOptions.SuppressAutoRecycle);
-            }
-
-            return element;
-        }
-
-        public void Add(Layoutable element, int dataIndex)
-        {
-            if (_realizedElements.Count == 0)
-            {
-                _firstRealizedDataIndex = dataIndex;
-            }
-
-            _realizedElements.Add(element);
-            _realizedElementLayoutBounds.Add(default);
-        }
-
-        public void Insert(int realizedIndex, int dataIndex, Layoutable? element)
-        {
-            if (realizedIndex == 0)
-            {
-                _firstRealizedDataIndex = dataIndex;
-            }
-
-            _realizedElements.Insert(realizedIndex, element);
-
-            // Set bounds to an invalid rect since we do not know it yet.
-            _realizedElementLayoutBounds.Insert(realizedIndex, new Rect(-1, -1, -1, -1));
-        }
-
-        public void ClearRealizedRange(int realizedIndex, int count)
-        {
-            for (int i = 0; i < count; i++)
-            {
-                // Clear from the edges so that ItemsRepeater can optimize on maintaining
-                // realized indices without walking through all the children every time.
-                int index = realizedIndex == 0 ? realizedIndex + i : (realizedIndex + count - 1) - i;
-                var elementRef = _realizedElements[index];
-
-                if (elementRef != null)
-                {
-                    _context!.RecycleElement(elementRef);
-                }
-            }
-
-            int endIndex = realizedIndex + count;
-            _realizedElements.RemoveRange(realizedIndex, endIndex - realizedIndex);
-            _realizedElementLayoutBounds.RemoveRange(realizedIndex, endIndex - realizedIndex);
-
-            if (realizedIndex == 0)
-            {
-                _firstRealizedDataIndex = _realizedElements.Count == 0 ?
-                    -1 : _firstRealizedDataIndex + count;
-            }
-        }
-
-        public void DiscardElementsOutsideWindow(bool forward, int startIndex)
-        {
-            // Remove layout elements that are outside the realized range.
-            if (IsDataIndexRealized(startIndex))
-            {
-                int rangeIndex = GetRealizedRangeIndexFromDataIndex(startIndex);
-
-                if (forward)
-                {
-                    ClearRealizedRange(rangeIndex, GetRealizedElementCount() - rangeIndex);
-                }
-                else
-                {
-                    ClearRealizedRange(0, rangeIndex + 1);
-                }
-            }
-        }
-
-        public void ClearRealizedRange() => ClearRealizedRange(0, GetRealizedElementCount());
-
-        public Rect GetLayoutBoundsForDataIndex(int dataIndex)
-        {
-            int realizedIndex = GetRealizedRangeIndexFromDataIndex(dataIndex);
-            return _realizedElementLayoutBounds[realizedIndex];
-        }
-
-        public void SetLayoutBoundsForDataIndex(int dataIndex, in Rect bounds)
-        {
-            int realizedIndex = GetRealizedRangeIndexFromDataIndex(dataIndex);
-            _realizedElementLayoutBounds[realizedIndex] = bounds;
-        }
-
-        public Rect GetLayoutBoundsForRealizedIndex(int realizedIndex) => _realizedElementLayoutBounds[realizedIndex];
-
-        public void SetLayoutBoundsForRealizedIndex(int realizedIndex, in Rect bounds)
-        {
-            _realizedElementLayoutBounds[realizedIndex] = bounds;
-        }
-
-        public bool IsDataIndexRealized(int index)
-        {
-            if (IsVirtualizingContext)
-            {
-                int realizedCount = GetRealizedElementCount();
-                return
-                    realizedCount > 0 &&
-                    GetDataIndexFromRealizedRangeIndex(0) <= index &&
-                    GetDataIndexFromRealizedRangeIndex(realizedCount - 1) >= index;
-            }
-            else
-            {
-                // Non virtualized - everything is realized
-                return index >= 0 && index < _context!.ItemCount;
-            }
-        }
-
-        public bool IsIndexValidInData(int currentIndex) => (uint)currentIndex < _context!.ItemCount;
-
-        public Layoutable? GetRealizedElement(int dataIndex)
-        {
-            return IsVirtualizingContext ?
-                GetAt(GetRealizedRangeIndexFromDataIndex(dataIndex)) :
-                _context!.GetOrCreateElementAt(
-                    dataIndex,
-                    ElementRealizationOptions.ForceCreate | ElementRealizationOptions.SuppressAutoRecycle);
-        }
-
-        public void EnsureElementRealized(bool forward, int dataIndex, string? layoutId)
-        {
-            if (IsDataIndexRealized(dataIndex) == false)
-            {
-                var element = _context!.GetOrCreateElementAt(
-                    dataIndex,
-                    ElementRealizationOptions.ForceCreate | ElementRealizationOptions.SuppressAutoRecycle);
-
-                if (forward)
-                {
-                    Add(element, dataIndex);
-                }
-                else
-                {
-                    Insert(0, dataIndex, element);
-                }
-
-                Logger.TryGet(LogEventLevel.Verbose, "Repeater")?.Log(this, "{LayoutId}: Created element for index {index}", layoutId, dataIndex);
-            }
-        }
-
-        public bool IsWindowConnected(in Rect window, ScrollOrientation orientation, bool scrollOrientationSameAsFlow)
-        {
-            bool intersects = false;
-
-            if (_realizedElementLayoutBounds.Count > 0)
-            {
-                var firstElementBounds = GetLayoutBoundsForRealizedIndex(0);
-                var lastElementBounds = GetLayoutBoundsForRealizedIndex(GetRealizedElementCount() - 1);
-
-                var effectiveOrientation = scrollOrientationSameAsFlow ?
-                    (orientation == ScrollOrientation.Vertical ? ScrollOrientation.Horizontal : ScrollOrientation.Vertical) :
-                    orientation;
-
-                var windowStart = effectiveOrientation == ScrollOrientation.Vertical ? window.Y : window.X;
-                var windowEnd = effectiveOrientation == ScrollOrientation.Vertical ? window.Y + window.Height : window.X + window.Width;
-                var firstElementStart = effectiveOrientation == ScrollOrientation.Vertical ? firstElementBounds.Y : firstElementBounds.X;
-                var lastElementEnd = effectiveOrientation == ScrollOrientation.Vertical ? lastElementBounds.Y + lastElementBounds.Height : lastElementBounds.X + lastElementBounds.Width;
-
-                intersects =
-                    firstElementStart <= windowEnd &&
-                    lastElementEnd >= windowStart;
-            }
-
-            return intersects;
-        }
-
-        public void DataSourceChanged(object? source, NotifyCollectionChangedEventArgs args)
-        {
-            if (_realizedElements.Count > 0)
-            {
-                switch (args.Action)
-                {
-                    case NotifyCollectionChangedAction.Add:
-                        {
-                            OnItemsAdded(args.NewStartingIndex, args.NewItems!.Count);
-                        }
-                        break;
-
-                    case NotifyCollectionChangedAction.Replace:
-                        {
-                            int oldSize = args.OldItems!.Count;
-                            int newSize = args.NewItems!.Count;
-                            int oldStartIndex = args.OldStartingIndex;
-                            int newStartIndex = args.NewStartingIndex;
-
-                            if (oldSize == newSize &&
-                                oldStartIndex == newStartIndex &&
-                                IsDataIndexRealized(oldStartIndex) &&
-                                IsDataIndexRealized(oldStartIndex + oldSize - 1))
-                            {
-                                // Straight up replace of n items within the realization window.
-                                // Removing and adding might causes us to lose the anchor causing us
-                                // to throw away all containers and start from scratch.
-                                // Instead, we can just clear those items and set the element to
-                                // null (sentinel) and let the next measure get new containers for them.
-                                var startRealizedIndex = GetRealizedRangeIndexFromDataIndex(oldStartIndex);
-                                for (int realizedIndex = startRealizedIndex; realizedIndex < startRealizedIndex + oldSize; realizedIndex++)
-                                {
-                                    var elementRef = _realizedElements[realizedIndex];
-
-                                    if (elementRef != null)
-                                    {
-                                        _context!.RecycleElement(elementRef);
-                                        _realizedElements[realizedIndex] = null;
-                                    }
-                                }
-                            }
-                            else
-                            {
-                                OnItemsRemoved(oldStartIndex, oldSize);
-                                OnItemsAdded(newStartIndex, newSize);
-                            }
-                        }
-                        break;
-
-                    // Remove clear all realized elements just to align the begavior
-                    // with ViewManager which resets realized item indices to defaults.
-                    // Freeing only removed items causes wrong indices to be stored
-                    // in virtualized info of items under some circumstances.
-                    case NotifyCollectionChangedAction.Remove:
-                    case NotifyCollectionChangedAction.Reset:
-                        ClearRealizedRange();
-                        break;
-
-                    case NotifyCollectionChangedAction.Move:
-                        int size = args.OldItems != null ? args.OldItems.Count : 1;
-                        OnItemsRemoved(args.OldStartingIndex, size);
-                        OnItemsAdded(args.NewStartingIndex, size);
-                        break;
-                }
-            }
-        }
-
-        public int GetElementDataIndex(Layoutable suggestedAnchor)
-        {
-            var it = _realizedElements.IndexOf(suggestedAnchor);
-            return it != -1 ? GetDataIndexFromRealizedRangeIndex(it) : -1;
-        }
-
-        public int GetDataIndexFromRealizedRangeIndex(int rangeIndex)
-        {
-            return IsVirtualizingContext ? rangeIndex + _firstRealizedDataIndex : rangeIndex;
-        }
-
-        private int GetRealizedRangeIndexFromDataIndex(int dataIndex)
-        {
-            return IsVirtualizingContext ? dataIndex - _firstRealizedDataIndex : dataIndex;
-        }
-
-        private void DiscardElementsOutsideWindow(in Rect window, ScrollOrientation orientation)
-        {
-            // The following illustration explains the cutoff indices.
-            // We will clear all the realized elements from both ends
-            // up to the corresponding cutoff index.
-            // '-' means the element is outside the cutoff range.
-            // '*' means the element is inside the cutoff range and will be cleared.
-            //
-            // Window:
-            //        |______________________________|
-            // Realization range:
-            // |*****----------------------------------*********|
-            //      |                                  |
-            //  frontCutoffIndex                backCutoffIndex
-            //
-            // Note that we tolerate at most one element outside of the window
-            // because the FlowLayoutAlgorithm.Generate routine stops *after*
-            // it laid out an element outside the realization window.
-            // This is also convenient because it protects the anchor
-            // during a BringIntoView operation during which the anchor may
-            // not be in the realization window (in fact, the realization window
-            // might be empty if the BringIntoView is issued before the first
-            // layout pass).
-
-            int realizedRangeSize = GetRealizedElementCount();
-            int frontCutoffIndex = -1;
-            int backCutoffIndex = realizedRangeSize;
-
-            for (int i = 0;
-                i < realizedRangeSize &&
-                !Intersects(window, _realizedElementLayoutBounds[i], orientation);
-                ++i)
-            {
-                ++frontCutoffIndex;
-            }
-
-            for (int i = realizedRangeSize - 1;
-                i >= 0 &&
-                !Intersects(window, _realizedElementLayoutBounds[i], orientation);
-                --i)
-            {
-                --backCutoffIndex;
-            }
-
-            if (backCutoffIndex < realizedRangeSize - 1)
-            {
-                ClearRealizedRange(backCutoffIndex + 1, realizedRangeSize - backCutoffIndex - 1);
-            }
-
-            if (frontCutoffIndex > 0)
-            {
-                ClearRealizedRange(0, Math.Min(frontCutoffIndex, GetRealizedElementCount()));
-            }
-        }
-
-        private static bool Intersects(in Rect lhs, in Rect rhs, ScrollOrientation orientation)
-        {
-            var lhsStart = orientation == ScrollOrientation.Vertical ? lhs.Y : lhs.X;
-            var lhsEnd = orientation == ScrollOrientation.Vertical ? lhs.Y + lhs.Height : lhs.X + lhs.Width;
-            var rhsStart = orientation == ScrollOrientation.Vertical ? rhs.Y : rhs.X;
-            var rhsEnd = orientation == ScrollOrientation.Vertical ? rhs.Y + rhs.Height : rhs.X + rhs.Width;
-
-            return lhsEnd >= rhsStart && lhsStart <= rhsEnd;
-        }
-
-        private void OnItemsAdded(int index, int count)
-        {
-            // Using the old indices here (before it was updated by the collection change)
-            // if the insert data index is between the first and last realized data index, we need
-            // to insert items.
-            int lastRealizedDataIndex = _firstRealizedDataIndex + GetRealizedElementCount() - 1;
-            int newStartingIndex = index;
-            if (newStartingIndex >= _firstRealizedDataIndex &&
-                newStartingIndex <= lastRealizedDataIndex)
-            {
-                // Inserted within the realized range
-                int insertRangeStartIndex = newStartingIndex - _firstRealizedDataIndex;
-                for (int i = 0; i < count; i++)
-                {
-                    // Insert null (sentinel) here instead of an element, that way we dont
-                    // end up creating a lot of elements only to be thrown out in the next layout.
-                    int insertRangeIndex = insertRangeStartIndex + i;
-                    int dataIndex = newStartingIndex + i;
-                    // This is to keep the contiguousness of the mapping
-                    Insert(insertRangeIndex, dataIndex, null);
-                }
-            }
-            else if (index <= _firstRealizedDataIndex)
-            {
-                // Items were inserted before the realized range.
-                // We need to update m_firstRealizedDataIndex;
-                _firstRealizedDataIndex += count;
-            }
-        }
-
-        private void OnItemsRemoved(int index, int count)
-        {
-            int lastRealizedDataIndex = _firstRealizedDataIndex + _realizedElements.Count - 1;
-            int startIndex = Math.Max(_firstRealizedDataIndex, index);
-            int endIndex = Math.Min(lastRealizedDataIndex, index + count - 1);
-            bool removeAffectsFirstRealizedDataIndex = (index <= _firstRealizedDataIndex);
-
-            if (endIndex >= startIndex)
-            {
-                ClearRealizedRange(GetRealizedRangeIndexFromDataIndex(startIndex), endIndex - startIndex + 1);
-            }
-
-            if (removeAffectsFirstRealizedDataIndex &&
-                _firstRealizedDataIndex != -1)
-            {
-                _firstRealizedDataIndex -= count;
-            }
-        }
-    }
-}

+ 0 - 767
src/Avalonia.Controls.ItemsRepeater/Layout/FlowLayoutAlgorithm.cs

@@ -1,767 +0,0 @@
-// This source file is adapted from the WinUI project.
-// (https://github.com/microsoft/microsoft-ui-xaml)
-//
-// Licensed to The Avalonia Project under MIT License, courtesy of The .NET Foundation.
-
-using System;
-using System.Collections.Specialized;
-using Avalonia.Logging;
-
-namespace Avalonia.Layout
-{
-    internal class FlowLayoutAlgorithm
-    {
-        private readonly OrientationBasedMeasures _orientation = new OrientationBasedMeasures();
-        private readonly ElementManager _elementManager = new ElementManager();
-        private Size _lastAvailableSize;
-        private double _lastItemSpacing;
-        private bool _collectionChangePending;
-        private VirtualizingLayoutContext? _context;
-        private IFlowLayoutAlgorithmDelegates? _algorithmCallbacks;
-        private Rect _lastExtent;
-        private int _firstRealizedDataIndexInsideRealizationWindow = -1;
-        private int _lastRealizedDataIndexInsideRealizationWindow = -1;
-
-        // If the scroll orientation is the same as the follow orientation
-        // we will only have one line since we will never wrap. In that case
-        // we do not want to align the line. We could potentially switch the
-        // meaning of line alignment in this case, but I'll hold off on that
-        // feature until someone asks for it - This is not a common scenario
-        // anyway. 
-        private bool _scrollOrientationSameAsFlow;
-
-        public Rect LastExtent => _lastExtent;
-
-        private bool IsVirtualizingContext
-        {
-            get
-            {
-                if (_context != null)
-                {
-                    var rect = _context.RealizationRect;
-                    bool hasInfiniteSize = double.IsInfinity(rect.Height) || double.IsInfinity(rect.Width);
-                    return !hasInfiniteSize;
-                }
-                return false;
-            }
-        }
-
-        private Rect RealizationRect => IsVirtualizingContext ? _context!.RealizationRect : new Rect(Size.Infinity);
-
-        public void InitializeForContext(VirtualizingLayoutContext context, IFlowLayoutAlgorithmDelegates callbacks)
-        {
-            _algorithmCallbacks = callbacks;
-            _context = context;
-            _elementManager.SetContext(context);
-        }
-
-        public void UninitializeForContext(VirtualizingLayoutContext context)
-        {
-            if (IsVirtualizingContext)
-            {
-                // This layout is about to be detached. Let go of all elements
-                // being held and remove the layout state from the context.
-                _elementManager.ClearRealizedRange();
-            }
-
-            context.LayoutState = null;
-        }
-
-        public Size Measure(
-            Size availableSize,
-            VirtualizingLayoutContext context,
-            bool isWrapping,
-            double minItemSpacing,
-            double lineSpacing,
-            int maxItemsPerLine,
-            ScrollOrientation orientation,
-            bool disableVirtualization,
-            string? layoutId)
-        {
-            _orientation.ScrollOrientation = orientation;
-
-            // If minor size is infinity, there is only one line and no need to align that line.
-            _scrollOrientationSameAsFlow = double.IsInfinity(_orientation.Minor(availableSize));
-            var realizationRect = RealizationRect;
-            Logger.TryGet(LogEventLevel.Verbose, "Repeater")?.Log(this, "{LayoutId}: MeasureLayout Realization({Rect})",
-                layoutId,
-                realizationRect);
-
-            var suggestedAnchorIndex = _context!.RecommendedAnchorIndex;
-            if (_elementManager.IsIndexValidInData(suggestedAnchorIndex))
-            {
-                var anchorRealized = _elementManager.IsDataIndexRealized(suggestedAnchorIndex);
-                if (!anchorRealized)
-                {
-                    MakeAnchor(_context, suggestedAnchorIndex, availableSize);
-                }
-            }
-
-            _elementManager.OnBeginMeasure(orientation);
-
-            int anchorIndex = GetAnchorIndex(availableSize, isWrapping, minItemSpacing, layoutId);
-            Generate(GenerateDirection.Forward, anchorIndex, availableSize, minItemSpacing, lineSpacing, maxItemsPerLine, disableVirtualization, layoutId);
-            Generate(GenerateDirection.Backward, anchorIndex, availableSize, minItemSpacing, lineSpacing, maxItemsPerLine, disableVirtualization, layoutId);
-            if (isWrapping && IsReflowRequired())
-            {
-                Logger.TryGet(LogEventLevel.Verbose, "Repeater")?.Log(this, "{LayoutId}: Reflow Pass", layoutId);
-                var firstElementBounds = _elementManager.GetLayoutBoundsForRealizedIndex(0);
-                _orientation.SetMinorStart(ref firstElementBounds, 0);
-                _elementManager.SetLayoutBoundsForRealizedIndex(0, firstElementBounds);
-                Generate(GenerateDirection.Forward, 0 /*anchorIndex*/, availableSize, minItemSpacing, lineSpacing, maxItemsPerLine, disableVirtualization, layoutId);
-            }
-
-            RaiseLineArranged();
-            _collectionChangePending = false;
-            _lastExtent = EstimateExtent(availableSize, layoutId);
-            SetLayoutOrigin();
-
-            return new Size(_lastExtent.Width, _lastExtent.Height);
-        }
-
-        public Size Arrange(
-            Size finalSize,
-            VirtualizingLayoutContext context,
-            bool isWrapping,
-            LineAlignment lineAlignment,
-            string? layoutId)
-        {
-            Logger.TryGet(LogEventLevel.Verbose, "Repeater")?.Log(this, "{LayoutId}: ArrangeLayout", layoutId);
-            ArrangeVirtualizingLayout(finalSize, lineAlignment, isWrapping, layoutId);
-
-            return new Size(
-                Math.Max(finalSize.Width, _lastExtent.Width),
-                Math.Max(finalSize.Height, _lastExtent.Height));
-        }
-
-        public void OnItemsSourceChanged(
-            object? source,
-            NotifyCollectionChangedEventArgs args,
-            VirtualizingLayoutContext context)
-        {
-            _elementManager.DataSourceChanged(source, args);
-            _collectionChangePending = true;
-        }
-
-        public Size MeasureElement(
-            Layoutable element,
-            int index,
-            Size availableSize,
-            VirtualizingLayoutContext context)
-        {
-            var measureSize = _algorithmCallbacks!.Algorithm_GetMeasureSize(index, availableSize, context);
-            element.Measure(measureSize);
-            var provisionalArrangeSize = _algorithmCallbacks.Algorithm_GetProvisionalArrangeSize(index, measureSize, element.DesiredSize, context);
-            _algorithmCallbacks.Algorithm_OnElementMeasured(element, index, availableSize, measureSize, element.DesiredSize, provisionalArrangeSize, context);
-
-            return provisionalArrangeSize; 
-        }
-
-        private int GetAnchorIndex(
-            Size availableSize,
-            bool isWrapping,
-            double minItemSpacing,
-            string? layoutId)
-        {
-            int anchorIndex = -1;
-            var anchorPosition= new Point();
-            var context = _context;
-
-            if (!IsVirtualizingContext)
-            {
-                // Non virtualizing host, start generating from the element 0
-                anchorIndex = context!.ItemCount > 0 ? 0 : -1;
-            }
-            else
-            {       
-                bool isRealizationWindowConnected = _elementManager.IsWindowConnected(RealizationRect, _orientation.ScrollOrientation, _scrollOrientationSameAsFlow);
-                // Item spacing and size in non-virtualizing direction change can cause elements to reflow
-                // and get a new column position. In that case we need the anchor to be positioned in the 
-                // correct column.
-                bool needAnchorColumnRevaluation = isWrapping && (
-                    _orientation.Minor(_lastAvailableSize) != _orientation.Minor(availableSize) ||
-                    _lastItemSpacing != minItemSpacing ||
-                    _collectionChangePending);
-
-                var suggestedAnchorIndex = _context!.RecommendedAnchorIndex;
-
-                var isAnchorSuggestionValid = suggestedAnchorIndex >= 0 &&
-                    _elementManager.IsDataIndexRealized(suggestedAnchorIndex);
-
-                if (isAnchorSuggestionValid)
-                {
-                    Logger.TryGet(LogEventLevel.Verbose, "Repeater")?.Log(this, "{LayoutId}: Using suggested anchor {Anchor}", layoutId, suggestedAnchorIndex);
-                    anchorIndex = _algorithmCallbacks!.Algorithm_GetAnchorForTargetElement(
-                        suggestedAnchorIndex,
-                        availableSize,
-                        context!).Index;
-
-                    if (_elementManager.IsDataIndexRealized(anchorIndex))
-                    {
-                        var anchorBounds = _elementManager.GetLayoutBoundsForDataIndex(anchorIndex);
-                        if (needAnchorColumnRevaluation)
-                        {
-                            // We were provided a valid anchor, but its position might be incorrect because for example it is in
-                            // the wrong column. We do know that the anchor is the first element in the row, so we can force the minor position
-                            // to start at 0.
-                            anchorPosition = _orientation.MinorMajorPoint(0, _orientation.MajorStart(anchorBounds));
-                        }
-                        else
-                        {
-                            anchorPosition = new Point(anchorBounds.X, anchorBounds.Y);
-                        }
-                    }
-                    else if (anchorIndex >= 0)
-                    {
-                        // It is possible to end up in a situation during a collection change where GetAnchorForTargetElement returns an index
-                        // which is not in the realized range. Eg. insert one item at index 0 for a grid layout. 
-                        // SuggestedAnchor will be 1 (used to be 0) and GetAnchorForTargetElement will return 0 (left most item in row). However 0 is not in the
-                        // realized range yet. In this case we realize the gap between the target anchor and the suggested anchor.
-                        int firstRealizedDataIndex = _elementManager.GetDataIndexFromRealizedRangeIndex(0);
-
-                        for (int i = firstRealizedDataIndex - 1; i >= anchorIndex; --i)
-                        {
-                            _elementManager.EnsureElementRealized(false /*forward*/, i, layoutId);
-                        }
-
-                        var anchorBounds = _elementManager.GetLayoutBoundsForDataIndex(suggestedAnchorIndex);
-                        anchorPosition = _orientation.MinorMajorPoint(0, _orientation.MajorStart(anchorBounds));
-                    }
-                }
-                else if (needAnchorColumnRevaluation || !isRealizationWindowConnected)
-                {
-                    if (needAnchorColumnRevaluation) { Logger.TryGet(LogEventLevel.Verbose, "Repeater")?.Log(this, "{LayoutId}: NeedAnchorColumnReevaluation", layoutId); }
-                    if (!isRealizationWindowConnected) { Logger.TryGet(LogEventLevel.Verbose, "Repeater")?.Log(this, "{LayoutId}: Disconnected Window", layoutId); }
-
-                    // The anchor is based on the realization window because a connected ItemsRepeater might intersect the realization window
-                    // but not the visible window. In that situation, we still need to produce a valid anchor.
-                    var anchorInfo = _algorithmCallbacks!.Algorithm_GetAnchorForRealizationRect(availableSize, context!);
-                    anchorIndex = anchorInfo.Index;
-                    anchorPosition = _orientation.MinorMajorPoint(0, anchorInfo.Offset);
-                }
-                else
-                {
-                    Logger.TryGet(LogEventLevel.Verbose, "Repeater")?.Log(this, "{LayoutId}: Connected Window - picking first realized element as anchor", layoutId);
-                    // No suggestion - just pick first in realized range
-                    anchorIndex = _elementManager.GetDataIndexFromRealizedRangeIndex(0);
-                    var firstElementBounds = _elementManager.GetLayoutBoundsForRealizedIndex(0);
-                    anchorPosition = new Point(firstElementBounds.X, firstElementBounds.Y);
-                }
-            }
-
-            Logger.TryGet(LogEventLevel.Verbose, "Repeater")?.Log(this, "{LayoutId}: Picked anchor: {Anchor}", layoutId, anchorIndex);
-            _firstRealizedDataIndexInsideRealizationWindow = _lastRealizedDataIndexInsideRealizationWindow = anchorIndex;
-            if (_elementManager.IsIndexValidInData(anchorIndex))
-            {
-                if (!_elementManager.IsDataIndexRealized(anchorIndex))
-                {
-                    // Disconnected, throw everything and create new anchor
-                    Logger.TryGet(LogEventLevel.Verbose, "Repeater")?.Log(this, "{LayoutId} Disconnected Window - throwing away all realized elements", layoutId);
-                    _elementManager.ClearRealizedRange();
-
-                    var anchor = _context!.GetOrCreateElementAt(anchorIndex, ElementRealizationOptions.ForceCreate | ElementRealizationOptions.SuppressAutoRecycle);
-                    _elementManager.Add(anchor, anchorIndex);
-                }
-
-                var anchorElement = _elementManager.GetRealizedElement(anchorIndex);
-                var desiredSize = MeasureElement(anchorElement!, anchorIndex, availableSize, _context!);
-                var layoutBounds = new Rect(anchorPosition.X, anchorPosition.Y, desiredSize.Width, desiredSize.Height);
-                _elementManager.SetLayoutBoundsForDataIndex(anchorIndex, layoutBounds);
-
-                Logger.TryGet(LogEventLevel.Verbose, "Repeater")?.Log(this, "{LayoutId}: Layout bounds of anchor {anchor} are ({Bounds})",
-                    layoutId,
-                    anchorIndex,
-                    layoutBounds);
-            }
-            else
-            {
-                // Throw everything away
-                Logger.TryGet(LogEventLevel.Verbose, "Repeater")?.Log(this, "{LayoutId} Anchor index is not valid - throwing away all realized elements",
-                    layoutId);
-                _elementManager.ClearRealizedRange();
-            }
-
-            // TODO: Perhaps we can track changes in the property setter
-            _lastAvailableSize = availableSize;
-            _lastItemSpacing = minItemSpacing;
-
-            return anchorIndex;
-        }
-
-        private void Generate(
-            GenerateDirection direction,
-            int anchorIndex,
-            Size availableSize,
-            double minItemSpacing,
-            double lineSpacing,
-            int maxItemsPerLine,
-            bool disableVirtualization,
-            string? layoutId)
-        {
-            if (anchorIndex != -1)
-            {
-                int step = (direction == GenerateDirection.Forward) ? 1 : -1;
-
-                Logger.TryGet(LogEventLevel.Verbose, "Repeater")?.Log(this, "{LayoutId}: Generating {Direction} from anchor {Anchor}",
-                    layoutId,
-                    direction,
-                    anchorIndex);
-
-                int previousIndex = anchorIndex;
-                int currentIndex = anchorIndex + step;
-                var anchorBounds = _elementManager.GetLayoutBoundsForDataIndex(anchorIndex);
-                var lineOffset = _orientation.MajorStart(anchorBounds);
-                var lineMajorSize = _orientation.MajorSize(anchorBounds);
-                var countInLine = 1;
-                int count = 0;
-                bool lineNeedsReposition = false;
-
-                while (_elementManager.IsIndexValidInData(currentIndex) &&
-                    (disableVirtualization || ShouldContinueFillingUpSpace(previousIndex, direction)))
-                {
-                    // Ensure layout element.
-                    _elementManager.EnsureElementRealized(direction == GenerateDirection.Forward, currentIndex, layoutId);
-                    var currentElement = _elementManager.GetRealizedElement(currentIndex);
-                    var desiredSize = MeasureElement(currentElement!, currentIndex, availableSize, _context!);
-                    ++count;
-
-                    // Lay it out.
-                    var previousElement = _elementManager.GetRealizedElement(previousIndex);
-                    var currentBounds = new Rect(0, 0, desiredSize.Width, desiredSize.Height);
-                    var previousElementBounds = _elementManager.GetLayoutBoundsForDataIndex(previousIndex);
-
-                    if (direction == GenerateDirection.Forward)
-                    {
-                        double remainingSpace = _orientation.Minor(availableSize) - (_orientation.MinorStart(previousElementBounds) + _orientation.MinorSize(previousElementBounds) + minItemSpacing + _orientation.Minor(desiredSize));
-                        if (countInLine >= maxItemsPerLine || _algorithmCallbacks!.Algorithm_ShouldBreakLine(currentIndex, remainingSpace))
-                        {
-                            // No more space in this row. wrap to next row.
-                            _orientation.SetMinorStart(ref currentBounds, 0);
-                            _orientation.SetMajorStart(ref currentBounds, _orientation.MajorStart(previousElementBounds) + lineMajorSize + lineSpacing);
-
-                            if (lineNeedsReposition)
-                            {
-                                // reposition the previous line (countInLine items)
-                                for (int i = 0; i < countInLine; i++)
-                                {
-                                    var dataIndex = currentIndex - 1 - i;
-                                    var bounds = _elementManager.GetLayoutBoundsForDataIndex(dataIndex);
-                                    _orientation.SetMajorSize(ref bounds, lineMajorSize);
-                                    _elementManager.SetLayoutBoundsForDataIndex(dataIndex, bounds);
-                                }
-                            }
-
-                            // Setup for next line.
-                            lineMajorSize = _orientation.MajorSize(currentBounds);
-                            lineOffset = _orientation.MajorStart(currentBounds);
-                            lineNeedsReposition = false;
-                            countInLine = 1;
-                        }
-                        else
-                        {
-                            // More space is available in this row.
-                            _orientation.SetMinorStart(ref currentBounds, _orientation.MinorStart(previousElementBounds) + _orientation.MinorSize(previousElementBounds) + minItemSpacing);
-                            _orientation.SetMajorStart(ref currentBounds, lineOffset);
-                            lineMajorSize = Math.Max(lineMajorSize, _orientation.MajorSize(currentBounds));
-                            lineNeedsReposition = _orientation.MajorSize(previousElementBounds) != _orientation.MajorSize(currentBounds);
-                            countInLine++;
-                        }
-                    }
-                    else
-                    {
-                        // Backward 
-                        double remainingSpace = _orientation.MinorStart(previousElementBounds) - (_orientation.Minor(desiredSize) + minItemSpacing);
-                        if (countInLine >= maxItemsPerLine || _algorithmCallbacks!.Algorithm_ShouldBreakLine(currentIndex, remainingSpace))
-                        {
-                            // Does not fit, wrap to the previous row
-                            var availableSizeMinor = _orientation.Minor(availableSize);
-
-                            _orientation.SetMinorStart(ref currentBounds, !double.IsInfinity(availableSizeMinor) ? availableSizeMinor - _orientation.Minor(desiredSize) : 0);
-                            _orientation.SetMajorStart(ref currentBounds, lineOffset - _orientation.Major(desiredSize) - lineSpacing);
-
-                            if (lineNeedsReposition)
-                            {
-                                var previousLineOffset = _orientation.MajorStart(_elementManager.GetLayoutBoundsForDataIndex(currentIndex + countInLine + 1));
-                                // reposition the previous line (countInLine items)
-                                for (int i = 0; i < countInLine; i++)
-                                {
-                                    var dataIndex = currentIndex + 1 + i;
-                                    if (dataIndex != anchorIndex)
-                                    {
-                                        var bounds = _elementManager.GetLayoutBoundsForDataIndex(dataIndex);
-                                        _orientation.SetMajorStart(ref bounds, previousLineOffset - lineMajorSize - lineSpacing);
-                                        _orientation.SetMajorSize(ref bounds, lineMajorSize);
-                                        _elementManager.SetLayoutBoundsForDataIndex(dataIndex, bounds);
-                                        Logger.TryGet(LogEventLevel.Verbose, "Repeater")?.Log(this, "{LayoutId}: Corrected Layout bounds of element {Index} are ({Bounds})",
-                                            layoutId,
-                                            dataIndex,
-                                            bounds);
-                                    }
-                                }
-                            }
-
-                            // Setup for next line.
-                            lineMajorSize = _orientation.MajorSize(currentBounds);
-                            lineOffset = _orientation.MajorStart(currentBounds);
-                            lineNeedsReposition = false;
-                            countInLine = 1;
-                        }
-                        else
-                        {
-                            // Fits in this row. put it in the previous position
-                            _orientation.SetMinorStart(ref currentBounds, _orientation.MinorStart(previousElementBounds) - _orientation.Minor(desiredSize) - minItemSpacing);
-                            _orientation.SetMajorStart(ref currentBounds, lineOffset);
-                            lineMajorSize = Math.Max(lineMajorSize, _orientation.MajorSize(currentBounds));
-                            lineNeedsReposition = _orientation.MajorSize(previousElementBounds) != _orientation.MajorSize(currentBounds);
-                            countInLine++;
-                        }
-                    }
-
-                    _elementManager.SetLayoutBoundsForDataIndex(currentIndex, currentBounds);
-
-                    Logger.TryGet(LogEventLevel.Verbose, "Repeater")?.Log(this, "{LayoutId}: Layout bounds of element {Index} are ({Bounds}).",
-                        layoutId,
-                        currentIndex,
-                        currentBounds);
-                    previousIndex = currentIndex;
-                    currentIndex += step;
-                }
-
-                // If we did not reach the top or bottom of the extent, we realized one 
-                // extra item before we knew we were outside the realization window. Do not
-                // account for that element in the indices inside the realization window.
-                if (direction == GenerateDirection.Forward)
-                {
-                    int dataCount = _context!.ItemCount;
-                    _lastRealizedDataIndexInsideRealizationWindow = previousIndex == dataCount - 1 ? dataCount - 1 : previousIndex - 1;
-                    _lastRealizedDataIndexInsideRealizationWindow = Math.Max(0, _lastRealizedDataIndexInsideRealizationWindow);
-                }
-                else
-                {
-                    int dataCount = _context!.ItemCount;
-                    _firstRealizedDataIndexInsideRealizationWindow = previousIndex == 0 ? 0 : previousIndex + 1;
-                    _firstRealizedDataIndexInsideRealizationWindow = Math.Min(dataCount - 1, _firstRealizedDataIndexInsideRealizationWindow);
-                }
-
-                _elementManager.DiscardElementsOutsideWindow(direction == GenerateDirection.Forward, currentIndex);
-            }
-        }
-
-        private void MakeAnchor(
-            VirtualizingLayoutContext context,
-            int index,
-            Size availableSize)
-        {
-            _elementManager.ClearRealizedRange();
-            // FlowLayout requires that the anchor is the first element in the row.
-            var internalAnchor = _algorithmCallbacks!.Algorithm_GetAnchorForTargetElement(index, availableSize, context);
-
-            // No need to set the position of the anchor.
-            // (0,0) is fine for now since the extent can
-            // grow in any direction.
-            for (int dataIndex = internalAnchor.Index; dataIndex < index + 1; ++dataIndex)
-            {
-                var element = context.GetOrCreateElementAt(dataIndex, ElementRealizationOptions.ForceCreate | ElementRealizationOptions.SuppressAutoRecycle);
-                element.Measure(_algorithmCallbacks.Algorithm_GetMeasureSize(dataIndex, availableSize, context));
-                _elementManager.Add(element, dataIndex);
-            }
-        }
-
-        private bool IsReflowRequired()
-        {
-            // If first element is realized and is not at the very beginning we need to reflow.
-            return
-                _elementManager.GetRealizedElementCount() > 0 &&
-                _elementManager.GetDataIndexFromRealizedRangeIndex(0) == 0 &&
-                _orientation.MinorStart(_elementManager.GetLayoutBoundsForRealizedIndex(0)) != 0;
-        }
-
-        private bool ShouldContinueFillingUpSpace(
-            int index,
-            GenerateDirection direction)
-        {
-            bool shouldContinue = false;
-            if (!IsVirtualizingContext)
-            {
-                shouldContinue = true;
-            }
-            else
-            {
-                var realizationRect = _context!.RealizationRect;
-                var elementBounds = _elementManager.GetLayoutBoundsForDataIndex(index);
-
-                var elementMajorStart = _orientation.MajorStart(elementBounds);
-                var elementMajorEnd = _orientation.MajorEnd(elementBounds);
-                var rectMajorStart = _orientation.MajorStart(realizationRect);
-                var rectMajorEnd = _orientation.MajorEnd(realizationRect);
-
-                var elementMinorStart = _orientation.MinorStart(elementBounds);
-                var elementMinorEnd = _orientation.MinorEnd(elementBounds);
-                var rectMinorStart = _orientation.MinorStart(realizationRect);
-                var rectMinorEnd = _orientation.MinorEnd(realizationRect);
-
-                // Ensure that both minor and major directions are taken into consideration so that if the scrolling direction
-                // is the same as the flow direction we still stop at the end of the viewport rectangle.
-                shouldContinue =
-                    (direction == GenerateDirection.Forward && elementMajorStart < rectMajorEnd && elementMinorStart < rectMinorEnd) ||
-                    (direction == GenerateDirection.Backward && elementMajorEnd > rectMajorStart && elementMinorEnd > rectMinorStart);
-            }
-
-            return shouldContinue;
-        }
-
-        private Rect EstimateExtent(Size availableSize, string? layoutId)
-        {
-            Layoutable? firstRealizedElement = null;
-            Rect firstBounds = new Rect();
-            Layoutable? lastRealizedElement = null;
-            Rect lastBounds = new Rect();
-            int firstDataIndex = -1;
-            int lastDataIndex = -1;
-
-            if (_elementManager.GetRealizedElementCount() > 0)
-            {
-                firstRealizedElement = _elementManager.GetAt(0);
-                firstBounds = _elementManager.GetLayoutBoundsForRealizedIndex(0);
-                firstDataIndex = _elementManager.GetDataIndexFromRealizedRangeIndex(0);
-
-                int last = _elementManager.GetRealizedElementCount() - 1;
-                lastRealizedElement = _elementManager.GetAt(last);
-                lastDataIndex = _elementManager.GetDataIndexFromRealizedRangeIndex(last);
-                lastBounds = _elementManager.GetLayoutBoundsForRealizedIndex(last);
-            }
-
-            Rect extent = _algorithmCallbacks!.Algorithm_GetExtent(
-                availableSize,
-                _context!,
-                firstRealizedElement,
-                firstDataIndex,
-                firstBounds,
-                lastRealizedElement,
-                lastDataIndex,
-                lastBounds);
-
-            Logger.TryGet(LogEventLevel.Verbose, "Repeater")?.Log(this, "{LayoutId} Extent: ({Bounds})", layoutId, extent);
-            return extent;
-        }
-
-        private void RaiseLineArranged()
-        {
-            var realizationRect = RealizationRect;
-            if (realizationRect.Width != 0.0f || realizationRect.Height != 0.0f)
-            {
-                int realizedElementCount = _elementManager.GetRealizedElementCount();
-                if (realizedElementCount > 0)
-                {
-                    int countInLine = 0;
-                    var previousElementBounds = _elementManager.GetLayoutBoundsForDataIndex(_firstRealizedDataIndexInsideRealizationWindow);
-                    var currentLineOffset = _orientation.MajorStart(previousElementBounds);
-                    var currentLineSize = _orientation.MajorSize(previousElementBounds);
-                    for (int currentDataIndex = _firstRealizedDataIndexInsideRealizationWindow; currentDataIndex <= _lastRealizedDataIndexInsideRealizationWindow; currentDataIndex++)
-                    {
-                        var currentBounds = _elementManager.GetLayoutBoundsForDataIndex(currentDataIndex);
-                        if (_orientation.MajorStart(currentBounds) != currentLineOffset)
-                        {
-                            // Staring a new line
-                            _algorithmCallbacks!.Algorithm_OnLineArranged(currentDataIndex - countInLine, countInLine, currentLineSize, _context!);
-                            countInLine = 0;
-                            currentLineOffset = _orientation.MajorStart(currentBounds);
-                            currentLineSize = 0;
-                        }
-
-                        currentLineSize = Math.Max(currentLineSize, _orientation.MajorSize(currentBounds));
-                        countInLine++;
-                        previousElementBounds = currentBounds;
-                    }
-
-                    // Raise for the last line.
-                    _algorithmCallbacks!.Algorithm_OnLineArranged(_lastRealizedDataIndexInsideRealizationWindow - countInLine + 1, countInLine, currentLineSize, _context!);
-                }
-            }
-        }
-
-        private void ArrangeVirtualizingLayout(
-            Size finalSize,
-            LineAlignment lineAlignment,
-            bool isWrapping,
-            string? layoutId)
-        {
-            // Walk through the realized elements one line at a time and 
-            // align them, Then call element.Arrange with the arranged bounds.
-            int realizedElementCount = _elementManager.GetRealizedElementCount();
-            if (realizedElementCount > 0)
-            {
-                var countInLine = 1;
-                var previousElementBounds = _elementManager.GetLayoutBoundsForRealizedIndex(0);
-                var currentLineOffset = _orientation.MajorStart(previousElementBounds);
-                var spaceAtLineStart = _orientation.MinorStart(previousElementBounds);
-                var spaceAtLineEnd = 0.0;
-                var currentLineSize = _orientation.MajorSize(previousElementBounds);
-                for (int i = 1; i < realizedElementCount; i++)
-                {
-                    var currentBounds = _elementManager.GetLayoutBoundsForRealizedIndex(i);
-                    if (_orientation.MajorStart(currentBounds) != currentLineOffset)
-                    {
-                        spaceAtLineEnd = _orientation.Minor(finalSize) - _orientation.MinorStart(previousElementBounds) - _orientation.MinorSize(previousElementBounds);
-                        PerformLineAlignment(i - countInLine, countInLine, spaceAtLineStart, spaceAtLineEnd, currentLineSize, lineAlignment, isWrapping, finalSize, layoutId);
-                        spaceAtLineStart = _orientation.MinorStart(currentBounds);
-                        countInLine = 0;
-                        currentLineOffset = _orientation.MajorStart(currentBounds);
-                        currentLineSize = 0;
-                    }
-
-                    countInLine++; // for current element
-                    currentLineSize = Math.Max(currentLineSize, _orientation.MajorSize(currentBounds));
-                    previousElementBounds = currentBounds;
-                }
-
-                // Last line - potentially have a property to customize
-                // aligning the last line or not.
-                if (countInLine > 0)
-                {
-                    var spaceAtEnd = _orientation.Minor(finalSize) - _orientation.MinorStart(previousElementBounds) - _orientation.MinorSize(previousElementBounds);
-                    PerformLineAlignment(realizedElementCount - countInLine, countInLine, spaceAtLineStart, spaceAtEnd, currentLineSize, lineAlignment, isWrapping, finalSize, layoutId);
-                }
-            }
-        }
-
-        // Align elements within a line. Note that this does not modify LayoutBounds. So if we get
-        // repeated measures, the LayoutBounds remain the same in each layout.
-        private void PerformLineAlignment(
-            int lineStartIndex,
-            int countInLine,
-            double spaceAtLineStart,
-            double spaceAtLineEnd,
-            double lineSize,
-            LineAlignment lineAlignment,
-            bool isWrapping,
-            Size finalSize,
-            string? layoutId)
-        {
-            for (int rangeIndex = lineStartIndex; rangeIndex < lineStartIndex + countInLine; ++rangeIndex)
-            {
-                var bounds = _elementManager.GetLayoutBoundsForRealizedIndex(rangeIndex);
-                _orientation.SetMajorSize(ref bounds, lineSize);
-
-                if (!_scrollOrientationSameAsFlow)
-                {
-                    // Note: Space at start could potentially be negative
-                    if (spaceAtLineStart != 0 || spaceAtLineEnd != 0)
-                    {
-                        var totalSpace = spaceAtLineStart + spaceAtLineEnd;
-                        var minorStart = _orientation.MinorStart(bounds);
-                        switch (lineAlignment)
-                        {
-                            case LineAlignment.Start:
-                                {
-                                    _orientation.SetMinorStart(ref bounds, minorStart - spaceAtLineStart);
-                                    break;
-                                }
-
-                            case LineAlignment.End:
-                                {
-                                    _orientation.SetMinorStart(ref bounds, minorStart + spaceAtLineEnd);
-                                    break;
-                                }
-
-                            case LineAlignment.Center:
-                                {
-                                    _orientation.SetMinorStart(ref bounds, (minorStart - spaceAtLineStart) + (totalSpace / 2));
-                                    break;
-                                }
-
-                            case LineAlignment.SpaceAround:
-                                {
-                                    var interItemSpace = countInLine >= 1 ? totalSpace / (countInLine * 2) : 0;
-                                    _orientation.SetMinorStart(
-                                        ref bounds, 
-                                        (minorStart - spaceAtLineStart) + (interItemSpace * ((rangeIndex - lineStartIndex + 1) * 2 - 1)));
-                                    break;
-                                }
-
-                            case LineAlignment.SpaceBetween:
-                                {
-                                    var interItemSpace = countInLine > 1 ? totalSpace / (countInLine - 1) : 0;
-                                    _orientation.SetMinorStart(
-                                        ref bounds,
-                                        (minorStart - spaceAtLineStart) + (interItemSpace * (rangeIndex - lineStartIndex)));
-                                    break;
-                                }
-
-                            case LineAlignment.SpaceEvenly:
-                                {
-                                    var interItemSpace = countInLine >= 1 ? totalSpace / (countInLine + 1) : 0;
-                                    _orientation.SetMinorStart(
-                                        ref bounds,
-                                        (minorStart - spaceAtLineStart) + (interItemSpace * (rangeIndex - lineStartIndex + 1)));
-                                    break;
-                                }
-                        }
-                    }
-                }
-
-                bounds = bounds.Translate(-_lastExtent.Position);
-
-                if (!isWrapping)
-                {
-                    _orientation.SetMinorSize(
-                        ref bounds,
-                        Math.Max(_orientation.MinorSize(bounds), _orientation.Minor(finalSize)));
-                }
-
-                var element = _elementManager.GetAt(rangeIndex);
-
-                Logger.TryGet(LogEventLevel.Verbose, "Repeater")?.Log(this, "{LayoutId}: Arranging element {Index} at ({Bounds})",
-                    layoutId,
-                    _elementManager.GetDataIndexFromRealizedRangeIndex(rangeIndex),
-                    bounds);
-                element.Arrange(bounds);
-            }
-        }
-
-        private void SetLayoutOrigin()
-        {
-            if (IsVirtualizingContext)
-            {
-                _context!.LayoutOrigin = new Point(_lastExtent.X, _lastExtent.Y);
-            }
-        }
-
-        public Layoutable? GetElementIfRealized(int dataIndex)
-        {
-            if (_elementManager.IsDataIndexRealized(dataIndex))
-            {
-                return _elementManager.GetRealizedElement(dataIndex);
-            }
-
-            return null;
-        }
-
-        public bool TryAddElement0(Layoutable element)
-        {
-            if (_elementManager.GetRealizedElementCount() == 0)
-            {
-                _elementManager.Add(element, 0);
-                return true;
-            }
-
-            return false;
-        }
-
-        public enum LineAlignment
-        {
-            Start,
-            Center,
-            End,
-            SpaceAround,
-            SpaceBetween,
-            SpaceEvenly,
-        }
-
-        private enum GenerateDirection
-        {
-            Forward,
-            Backward,
-        }
-    }
-}

+ 0 - 44
src/Avalonia.Controls.ItemsRepeater/Layout/IFlowLayoutAlgorithmDelegates.cs

@@ -1,44 +0,0 @@
-// This source file is adapted from the WinUI project.
-// (https://github.com/microsoft/microsoft-ui-xaml)
-//
-// Licensed to The Avalonia Project under MIT License, courtesy of The .NET Foundation.
-
-namespace Avalonia.Layout
-{
-    internal struct FlowLayoutAnchorInfo
-    {
-        public int Index { get; set; }
-        public double Offset { get; set; }
-    }
-
-    internal interface IFlowLayoutAlgorithmDelegates
-    {
-        Size Algorithm_GetMeasureSize(int index, Size availableSize, VirtualizingLayoutContext context);
-        Size Algorithm_GetProvisionalArrangeSize(int index, Size measureSize, Size desiredSize, VirtualizingLayoutContext context);
-        bool Algorithm_ShouldBreakLine(int index, double remainingSpace);
-        FlowLayoutAnchorInfo Algorithm_GetAnchorForRealizationRect(Size availableSize, VirtualizingLayoutContext context);
-        FlowLayoutAnchorInfo Algorithm_GetAnchorForTargetElement(int targetIndex, Size availableSize, VirtualizingLayoutContext context);
-        Rect Algorithm_GetExtent(
-            Size availableSize,
-            VirtualizingLayoutContext context,
-            Layoutable? firstRealized,
-            int firstRealizedItemIndex,
-            Rect firstRealizedLayoutBounds,
-            Layoutable? lastRealized,
-            int lastRealizedItemIndex,
-            Rect lastRealizedLayoutBounds);
-        void Algorithm_OnElementMeasured(
-            Layoutable element,
-            int index,
-            Size availableSize,
-            Size measureSize,
-            Size desiredSize,
-            Size provisionalArrangeSize,
-            VirtualizingLayoutContext context);
-        void Algorithm_OnLineArranged(
-            int startIndex,
-            int countInLine,
-            double lineSize,
-            VirtualizingLayoutContext context);
-    }
-}

+ 0 - 28
src/Avalonia.Controls.ItemsRepeater/Layout/LayoutContext.cs

@@ -1,28 +0,0 @@
-// This source file is adapted from the WinUI project.
-// (https://github.com/microsoft/microsoft-ui-xaml)
-//
-// Licensed to The Avalonia Project under MIT License, courtesy of The .NET Foundation.
-
-namespace Avalonia.Layout
-{
-    /// <summary>
-    /// Represents the base class for an object that facilitates communication between an attached
-    /// layout and its host container.
-    /// </summary>
-    public class LayoutContext : AvaloniaObject
-    {
-        /// <summary>
-        /// Gets or sets an object that represents the state of a layout.
-        /// </summary>
-        public object? LayoutState 
-        {
-            get => LayoutStateCore;
-            set => LayoutStateCore = value;
-        }
-
-        /// <summary>
-        /// Implements the behavior of <see cref="LayoutState"/> in a derived or custom LayoutContext.
-        /// </summary>
-        protected virtual object? LayoutStateCore { get; set; }
-    }
-}

+ 0 - 45
src/Avalonia.Controls.ItemsRepeater/Layout/LayoutContextAdapter.cs

@@ -1,45 +0,0 @@
-// This source file is adapted from the WinUI project.
-// (https://github.com/microsoft/microsoft-ui-xaml)
-//
-// Licensed to The Avalonia Project under MIT License, courtesy of The .NET Foundation.
-
-using System;
-
-namespace Avalonia.Layout
-{
-    internal class LayoutContextAdapter : VirtualizingLayoutContext
-    {
-        private readonly NonVirtualizingLayoutContext _nonVirtualizingContext;
-
-        public LayoutContextAdapter(NonVirtualizingLayoutContext nonVirtualizingContext)
-        {
-            _nonVirtualizingContext = nonVirtualizingContext;
-        }
-
-        protected override object? LayoutStateCore 
-        { 
-            get => _nonVirtualizingContext.LayoutState;
-            set => _nonVirtualizingContext.LayoutState = value; 
-        }
-
-        protected override Point LayoutOriginCore 
-        {
-            get => default;
-            set 
-            { 
-                if (value != default)
-                {
-                    throw new InvalidOperationException("LayoutOrigin must be at (0,0) when RealizationRect is infinite sized.");
-                }
-            }
-        }
-
-        protected override Rect RealizationRectCore() => new Rect(Size.Infinity);
-
-        protected override int ItemCountCore() => _nonVirtualizingContext.Children.Count;
-        protected override object GetItemAtCore(int index) => _nonVirtualizingContext.Children[index];
-        protected override Layoutable GetOrCreateElementAtCore(int index, ElementRealizationOptions options) =>
-            _nonVirtualizingContext.Children[index];
-        protected override void RecycleElementCore(Layoutable element) { }
-    }
-}

+ 0 - 83
src/Avalonia.Controls.ItemsRepeater/Layout/NonVirtualizingLayout.cs

@@ -1,83 +0,0 @@
-// This source file is adapted from the WinUI project.
-// (https://github.com/microsoft/microsoft-ui-xaml)
-//
-// Licensed to The Avalonia Project under MIT License, courtesy of The .NET Foundation.
-
-namespace Avalonia.Layout
-{
-    /// <summary>
-    /// Represents the base class for an object that sizes and arranges child elements for a host
-    /// and and does not support virtualization.
-    /// </summary>
-    /// <remarks>
-    /// NonVirtualizingLayout is the base class for layouts that do not support virtualization. You
-    /// can inherit from it to create your own layout.
-    /// 
-    /// A non-virtualizing layout can measure and arrange child elements.
-    /// </remarks>
-    public abstract class NonVirtualizingLayout : AttachedLayout
-    {
-        /// <summary>
-        /// When overridden in a derived class, initializes any per-container state the layout
-        /// requires when it is attached to an ILayoutable container.
-        /// </summary>
-        /// <param name="context">
-        /// The context object that facilitates communication between the layout and its host
-        /// container.
-        /// </param>
-        protected internal virtual void InitializeForContextCore(LayoutContext context)
-        {
-        }
-
-        /// <summary>
-        /// When overridden in a derived class, removes any state the layout previously stored on
-        /// the ILayoutable container.
-        /// </summary>
-        /// <param name="context">
-        /// The context object that facilitates communication between the layout and its host
-        /// container.
-        /// </param>
-        protected internal virtual void UninitializeForContextCore(LayoutContext context)
-        {
-        }
-
-        /// <summary>
-        /// Provides the behavior for the "Measure" pass of the layout cycle. Classes can override
-        /// this method to define their own "Measure" pass behavior.
-        /// </summary>
-        /// <param name="context">
-        /// The context object that facilitates communication between the layout and its host
-        /// container.
-        /// </param>
-        /// <param name="availableSize">
-        /// The available size that this object can give to child objects. Infinity can be
-        /// specified as a value to indicate that the object will size to whatever content is
-        /// available.
-        /// </param>
-        /// <returns>
-        /// The size that this object determines it needs during layout, based on its calculations
-        /// of the allocated sizes for child objects or based on other considerations such as a
-        /// fixed container size.
-        /// </returns>
-        protected internal abstract Size MeasureOverride(
-            NonVirtualizingLayoutContext context,
-            Size availableSize);
-
-        /// <summary>
-        /// When implemented in a derived class, provides the behavior for the "Arrange" pass of
-        /// layout. Classes can override this method to define their own "Arrange" pass behavior.
-        /// </summary>
-        /// <param name="context">
-        /// The context object that facilitates communication between the layout and its host
-        /// container.
-        /// </param>
-        /// <param name="finalSize">
-        /// The final area within the container that this object should use to arrange itself and
-        /// its children.
-        /// </param>
-        /// <returns>The actual size that is used after the element is arranged in layout.</returns>
-        protected internal virtual Size ArrangeOverride(
-            NonVirtualizingLayoutContext context,
-            Size finalSize) => finalSize;
-    }
-}

+ 0 - 31
src/Avalonia.Controls.ItemsRepeater/Layout/NonVirtualizingLayoutContext.cs

@@ -1,31 +0,0 @@
-// This source file is adapted from the WinUI project.
-// (https://github.com/microsoft/microsoft-ui-xaml)
-//
-// Licensed to The Avalonia Project under MIT License, courtesy of The .NET Foundation.
-
-using System.Collections.Generic;
-
-namespace Avalonia.Layout
-{
-    /// <summary>
-    /// Represents the base class for layout context types that do not support virtualization.
-    /// </summary>
-    public abstract class NonVirtualizingLayoutContext : LayoutContext
-    {
-        private VirtualizingLayoutContext? _contextAdapter;
-
-        /// <summary>
-        /// Gets the collection of child controls from the container that provides the context.
-        /// </summary>
-        public IReadOnlyList<Layoutable> Children => ChildrenCore;
-
-        /// <summary>
-        /// Implements the behavior for getting the return value of <see cref="Children"/> in a
-        /// derived or custom <see cref="NonVirtualizingLayoutContext"/>.
-        /// </summary>
-        protected abstract IReadOnlyList<Layoutable> ChildrenCore { get; }
-
-        internal VirtualizingLayoutContext GetVirtualizingContextAdapter() =>
-            _contextAdapter ??= new LayoutContextAdapter(this);
-    }
-}

+ 0 - 162
src/Avalonia.Controls.ItemsRepeater/Layout/NonVirtualizingStackLayout.cs

@@ -1,162 +0,0 @@
-using System;
-using System.Collections.Generic;
-using System.Text;
-using Avalonia.Data;
-
-namespace Avalonia.Layout
-{
-    public class NonVirtualizingStackLayout : NonVirtualizingLayout
-    {
-        /// <summary>
-        /// Defines the <see cref="Orientation"/> property.
-        /// </summary>
-        public static readonly StyledProperty<Orientation> OrientationProperty =
-            StackLayout.OrientationProperty.AddOwner<NonVirtualizingStackLayout>();
-
-        /// <summary>
-        /// Defines the <see cref="Spacing"/> property.
-        /// </summary>
-        public static readonly StyledProperty<double> SpacingProperty =
-            StackLayout.SpacingProperty.AddOwner<NonVirtualizingStackLayout>();
-
-        /// <summary>
-        /// Gets or sets the axis along which items are laid out.
-        /// </summary>
-        /// <value>
-        /// One of the enumeration values that specifies the axis along which items are laid out.
-        /// The default is Vertical.
-        /// </value>
-        public Orientation Orientation
-        {
-            get => GetValue(OrientationProperty);
-            set => SetValue(OrientationProperty, value);
-        }
-
-        /// <summary>
-        /// Gets or sets a uniform distance (in pixels) between stacked items. It is applied in the
-        /// direction of the StackLayout's Orientation.
-        /// </summary>
-        public double Spacing
-        {
-            get => GetValue(SpacingProperty);
-            set => SetValue(SpacingProperty, value);
-        }
-
-        protected internal override Size MeasureOverride(
-            NonVirtualizingLayoutContext context,
-            Size availableSize)
-        {
-            var extentU = 0.0;
-            var extentV = 0.0;
-            var childCount = context.Children.Count;
-            var isVertical = Orientation == Orientation.Vertical;
-            var spacing = Spacing;
-            var constraint = isVertical ?
-                availableSize.WithHeight(double.PositiveInfinity) :
-                availableSize.WithWidth(double.PositiveInfinity);
-
-            for (var i = 0; i < childCount; ++i)
-            {
-                var element = context.Children[i];
-
-                if ((element as Visual)?.IsVisible == false)
-                {
-                    continue;
-                }
-
-                element.Measure(constraint);
-                
-                if (isVertical)
-                {
-                    extentU += element.DesiredSize.Height;
-                    extentV = Math.Max(extentV, element.DesiredSize.Width);
-                }
-                else
-                {
-                    extentU += element.DesiredSize.Width;
-                    extentV = Math.Max(extentV, element.DesiredSize.Height);
-                }
-
-                if (i < childCount - 1)
-                {
-                    extentU += spacing;
-                }
-            }
-
-            return isVertical ? new Size(extentV, extentU) : new Size(extentU, extentV);
-        }
-
-        protected internal override Size ArrangeOverride(
-            NonVirtualizingLayoutContext context,
-            Size finalSize)
-        {
-            var u = 0.0;
-            var childCount = context.Children.Count;
-            var isVertical = Orientation == Orientation.Vertical;
-            var spacing = Spacing;
-            var bounds = new Rect();
-
-            for (var i = 0; i < childCount; ++i)
-            {
-                var element = context.Children[i];
-
-                if ((element as Visual)?.IsVisible == false)
-                {
-                    continue;
-                }
-
-                bounds = isVertical ?
-                    LayoutVertical(element, u, finalSize) :
-                    LayoutHorizontal(element, u, finalSize);
-                element.Arrange(bounds);
-                u = (isVertical ? bounds.Bottom : bounds.Right) + spacing;
-            }
-
-            return new Size(
-                Math.Max(finalSize.Width, bounds.Width),
-                Math.Max(finalSize.Height, bounds.Height));
-        }
-
-        private static Rect LayoutVertical(Layoutable element, double y, Size constraint)
-        {
-            var x = 0.0;
-            var width = element.DesiredSize.Width;
-
-            switch (element.HorizontalAlignment)
-            {
-                case HorizontalAlignment.Center:
-                    x += (constraint.Width - element.DesiredSize.Width) / 2;
-                    break;
-                case HorizontalAlignment.Right:
-                    x += constraint.Width - element.DesiredSize.Width;
-                    break;
-                case HorizontalAlignment.Stretch:
-                    width = constraint.Width;
-                    break;
-            }
-
-            return new Rect(x, y, width, element.DesiredSize.Height);
-        }
-
-        private static Rect LayoutHorizontal(Layoutable element, double x, Size constraint)
-        {
-            var y = 0.0;
-            var height = element.DesiredSize.Height;
-
-            switch (element.VerticalAlignment)
-            {
-                case VerticalAlignment.Center:
-                    y += (constraint.Height - element.DesiredSize.Height) / 2;
-                    break;
-                case VerticalAlignment.Bottom:
-                    y += constraint.Height - element.DesiredSize.Height;
-                    break;
-                case VerticalAlignment.Stretch:
-                    height = constraint.Height;
-                    break;
-            }
-
-            return new Rect(x, y, element.DesiredSize.Width, height);
-        }
-    }
-}

+ 0 - 96
src/Avalonia.Controls.ItemsRepeater/Layout/OrientationBasedMeasures.cs

@@ -1,96 +0,0 @@
-// This source file is adapted from the WinUI project.
-// (https://github.com/microsoft/microsoft-ui-xaml)
-//
-// Licensed to The Avalonia Project under MIT License, courtesy of The .NET Foundation.
-
-namespace Avalonia.Layout
-{
-    internal enum ScrollOrientation
-    {
-        Vertical,
-        Horizontal,
-    }
-
-    internal class OrientationBasedMeasures
-    {
-        public ScrollOrientation ScrollOrientation { get; set; } = ScrollOrientation.Vertical;
-
-        public double Major(in Size size) => ScrollOrientation == ScrollOrientation.Vertical ? size.Height : size.Width;
-        public double Minor(in Size size) => ScrollOrientation == ScrollOrientation.Vertical ? size.Width : size.Height;
-        public double MajorSize(in Rect rect) => ScrollOrientation == ScrollOrientation.Vertical ? rect.Height : rect.Width;
-        public double MinorSize(in Rect rect) => ScrollOrientation == ScrollOrientation.Vertical ? rect.Width : rect.Height;
-        public double MajorStart(in Rect rect) => ScrollOrientation == ScrollOrientation.Vertical ? rect.Y : rect.X;
-        public double MinorStart(in Rect rect) => ScrollOrientation == ScrollOrientation.Vertical ? rect.X : rect.Y;
-        public double MajorEnd(in Rect rect) => ScrollOrientation == ScrollOrientation.Vertical ? rect.Bottom : rect.Right;
-        public double MinorEnd(in Rect rect) => ScrollOrientation == ScrollOrientation.Vertical ? rect.Right : rect.Bottom;
-
-        public void SetMajorSize(ref Rect rect, double value)
-        {
-            if (ScrollOrientation == ScrollOrientation.Vertical)
-            {
-                rect = rect.WithHeight(value);
-            }
-            else
-            {
-                rect = rect.WithWidth(value);
-            }
-        }
-
-        public void SetMinorSize(ref Rect rect, double value)
-        {
-            if (ScrollOrientation == ScrollOrientation.Vertical)
-            {
-                rect = rect.WithWidth(value);
-            }
-            else
-            {
-                rect = rect.WithHeight(value);
-            }
-        }
-
-        public void SetMajorStart(ref Rect rect, double value)
-        {
-            if (ScrollOrientation == ScrollOrientation.Vertical)
-            {
-                rect = rect.WithY(value);
-            }
-            else
-            {
-                rect = rect.WithX(value);
-            }
-        }
-
-        public void SetMinorStart(ref Rect rect, double value)
-        {
-            if (ScrollOrientation == ScrollOrientation.Vertical)
-            {
-                rect = rect.WithX(value);
-            }
-            else
-            {
-                rect = rect.WithY(value);
-            }
-        }
-
-        public Rect MinorMajorRect(double minor, double major, double minorSize, double majorSize)
-        {
-            return ScrollOrientation == ScrollOrientation.Vertical ?
-                new Rect(minor, major, minorSize, majorSize) :
-                new Rect(major, minor, majorSize, minorSize);
-        }
-
-        public Point MinorMajorPoint(double minor, double major)
-        {
-            return ScrollOrientation == ScrollOrientation.Vertical ?
-                new Point(minor, major) :
-                new Point(major, minor);
-        }
-
-        public Size MinorMajorSize(double minor, double major)
-        {
-            return ScrollOrientation == ScrollOrientation.Vertical ?
-                new Size(minor, major) :
-                new Size(major, minor);
-        }
-    }
-}

+ 0 - 365
src/Avalonia.Controls.ItemsRepeater/Layout/StackLayout.cs

@@ -1,365 +0,0 @@
-// This source file is adapted from the WinUI project.
-// (https://github.com/microsoft/microsoft-ui-xaml)
-//
-// Licensed to The Avalonia Project under MIT License, courtesy of The .NET Foundation.
-
-using System;
-using System.Collections.Specialized;
-using Avalonia.Controls;
-using Avalonia.Data;
-using Avalonia.Logging;
-
-namespace Avalonia.Layout
-{
-    /// <summary>
-    /// Arranges elements into a single line (with spacing) that can be oriented horizontally or vertically.
-    /// </summary>
-    public class StackLayout : VirtualizingLayout, IFlowLayoutAlgorithmDelegates
-    {
-        /// <summary>
-        /// Defines the <see cref="DisableVirtualization"/> property.
-        /// </summary>
-        public static readonly StyledProperty<bool> DisableVirtualizationProperty =
-            AvaloniaProperty.Register<StackLayout, bool>(nameof(DisableVirtualization));
-
-        /// <summary>
-        /// Defines the <see cref="Orientation"/> property.
-        /// </summary>
-        public static readonly StyledProperty<Orientation> OrientationProperty =
-            StackPanel.OrientationProperty.AddOwner<StackLayout>();
-
-        /// <summary>
-        /// Defines the <see cref="Spacing"/> property.
-        /// </summary>
-        public static readonly StyledProperty<double> SpacingProperty =
-            StackPanel.SpacingProperty.AddOwner<StackLayout>();
-
-        private readonly OrientationBasedMeasures _orientation = new OrientationBasedMeasures();
-
-        /// <summary>
-        /// Initializes a new instance of the StackLayout class.
-        /// </summary>
-        public StackLayout()
-        {
-            LayoutId = "StackLayout";
-        }
-
-        /// <summary>
-        /// Gets or sets a value indicating whether virtualization is disabled on the layout.
-        /// </summary>
-        public bool DisableVirtualization
-        {
-            get => GetValue(DisableVirtualizationProperty);
-            set => SetValue(DisableVirtualizationProperty, value);
-        }
-
-        /// <summary>
-        /// Gets or sets the axis along which items are laid out.
-        /// </summary>
-        /// <value>
-        /// One of the enumeration values that specifies the axis along which items are laid out.
-        /// The default is Vertical.
-        /// </value>
-        public Orientation Orientation
-        {
-            get => GetValue(OrientationProperty);
-            set => SetValue(OrientationProperty, value);
-        }
-
-        /// <summary>
-        /// Gets or sets a uniform distance (in pixels) between stacked items. It is applied in the
-        /// direction of the StackLayout's Orientation.
-        /// </summary>
-        public double Spacing
-        {
-            get => GetValue(SpacingProperty);
-            set => SetValue(SpacingProperty, value);
-        }
-
-        internal Rect GetExtent(
-            Size availableSize,
-            VirtualizingLayoutContext context,
-            Layoutable? firstRealized,
-            int firstRealizedItemIndex,
-            Rect firstRealizedLayoutBounds,
-            Layoutable? lastRealized,
-            int lastRealizedItemIndex,
-            Rect lastRealizedLayoutBounds)
-        {
-            var extent = new Rect();
-
-            // Constants
-            int itemsCount = context.ItemCount;
-            var stackState = (StackLayoutState)context.LayoutState!;
-            double averageElementSize = GetAverageElementSize(availableSize, context, stackState) + Spacing;
-
-            _orientation.SetMinorSize(ref extent, stackState.MaxArrangeBounds);
-            _orientation.SetMajorSize(ref extent, Math.Max(0.0f, itemsCount * averageElementSize - Spacing));
-            if (itemsCount > 0)
-            {
-                if (firstRealized != null)
-                {
-                    _orientation.SetMajorStart(
-                        ref extent,
-                        _orientation.MajorStart(firstRealizedLayoutBounds) - firstRealizedItemIndex * averageElementSize);
-                    var remainingItems = itemsCount - lastRealizedItemIndex - 1;
-                    _orientation.SetMajorSize(
-                        ref extent,
-                        _orientation.MajorEnd(lastRealizedLayoutBounds) -
-                            _orientation.MajorStart(extent) + 
-                            (remainingItems * averageElementSize));
-                }
-                else
-                {
-                    Logger.TryGet(LogEventLevel.Verbose, "Repeater")?.Log(this, "{LayoutId}: Estimating extent with no realized elements",
-                        LayoutId);
-                }
-            }
-
-            Logger.TryGet(LogEventLevel.Verbose, "Repeater")?.Log(this, "{LayoutId}: Extent is ({Size}). Based on average {Average}",
-                LayoutId, extent.Size, averageElementSize);
-            return extent;
-        }
-
-        internal void OnElementMeasured(
-            Layoutable element,
-            int index,
-            Size availableSize,
-            Size measureSize,
-            Size desiredSize,
-            Size provisionalArrangeSize,
-            VirtualizingLayoutContext context)
-        {
-            if (context is VirtualizingLayoutContext virtualContext)
-            {
-                var stackState = (StackLayoutState)virtualContext.LayoutState!;
-                var provisionalArrangeSizeWinRt = provisionalArrangeSize;
-                stackState.OnElementMeasured(
-                    index,
-                    _orientation.Major(provisionalArrangeSizeWinRt),
-                    _orientation.Minor(provisionalArrangeSizeWinRt));
-            }
-        }
-
-        Size IFlowLayoutAlgorithmDelegates.Algorithm_GetMeasureSize(
-            int index,
-            Size availableSize,
-            VirtualizingLayoutContext context) => availableSize;
-
-        Size IFlowLayoutAlgorithmDelegates.Algorithm_GetProvisionalArrangeSize(
-            int index,
-            Size measureSize,
-            Size desiredSize,
-            VirtualizingLayoutContext context)
-        {
-            var measureSizeMinor = _orientation.Minor(measureSize);
-            return _orientation.MinorMajorSize(
-                !double.IsInfinity(measureSizeMinor) ?
-                    Math.Max(measureSizeMinor, _orientation.Minor(desiredSize)) :
-                    _orientation.Minor(desiredSize),
-                _orientation.Major(desiredSize));
-        }
-
-        bool IFlowLayoutAlgorithmDelegates.Algorithm_ShouldBreakLine(int index, double remainingSpace) => true;
-
-        FlowLayoutAnchorInfo IFlowLayoutAlgorithmDelegates.Algorithm_GetAnchorForRealizationRect(
-            Size availableSize,
-            VirtualizingLayoutContext context) => GetAnchorForRealizationRect(availableSize, context);
-
-        FlowLayoutAnchorInfo IFlowLayoutAlgorithmDelegates.Algorithm_GetAnchorForTargetElement(
-            int targetIndex,
-            Size availableSize,
-            VirtualizingLayoutContext context)
-        {
-            double offset = double.NaN;
-            int index = -1;
-            int itemsCount = context.ItemCount;
-
-            if (targetIndex >= 0 && targetIndex < itemsCount)
-            {
-                index = targetIndex;
-                var state = (StackLayoutState)context.LayoutState!;
-                double averageElementSize = GetAverageElementSize(availableSize, context, state) + Spacing;
-                offset = index * averageElementSize + _orientation.MajorStart(state.FlowAlgorithm.LastExtent);
-            }
-
-            return new FlowLayoutAnchorInfo { Index = index, Offset = offset };
-        }
-
-        Rect IFlowLayoutAlgorithmDelegates.Algorithm_GetExtent(
-            Size availableSize,
-            VirtualizingLayoutContext context,
-            Layoutable? firstRealized,
-            int firstRealizedItemIndex,
-            Rect firstRealizedLayoutBounds,
-            Layoutable? lastRealized,
-            int lastRealizedItemIndex,
-            Rect lastRealizedLayoutBounds)
-        {
-            return GetExtent(
-                availableSize,
-                context,
-                firstRealized,
-                firstRealizedItemIndex,
-                firstRealizedLayoutBounds,
-                lastRealized,
-                lastRealizedItemIndex,
-                lastRealizedLayoutBounds);
-        }
-
-        void IFlowLayoutAlgorithmDelegates.Algorithm_OnElementMeasured(Layoutable element, int index, Size availableSize, Size measureSize, Size desiredSize, Size provisionalArrangeSize, VirtualizingLayoutContext context)
-        {
-            OnElementMeasured(
-                element,
-                index,
-                availableSize,
-                measureSize,
-                desiredSize,
-                provisionalArrangeSize,
-                context);
-        }
-
-        void IFlowLayoutAlgorithmDelegates.Algorithm_OnLineArranged(int startIndex, int countInLine, double lineSize, VirtualizingLayoutContext context)
-        {
-        }
-
-        internal FlowLayoutAnchorInfo GetAnchorForRealizationRect(
-            Size availableSize,
-            VirtualizingLayoutContext context)
-        {
-            int anchorIndex = -1;
-            double offset = double.NaN;
-
-            // Constants
-            int itemsCount = context.ItemCount;
-            if (itemsCount > 0)
-            {
-                var realizationRect = context.RealizationRect;
-                var state = (StackLayoutState)context.LayoutState!;
-                var lastExtent = state.FlowAlgorithm.LastExtent;
-
-                double averageElementSize = GetAverageElementSize(availableSize, context, state) + Spacing;
-                double realizationWindowOffsetInExtent = _orientation.MajorStart(realizationRect) - _orientation.MajorStart(lastExtent);
-                double majorSize = _orientation.MajorSize(lastExtent) == 0 ? Math.Max(0.0, averageElementSize * itemsCount - Spacing) : _orientation.MajorSize(lastExtent);
-                if (itemsCount > 0 &&
-                    _orientation.MajorSize(realizationRect) >= 0 &&
-                    // MajorSize = 0 will account for when a nested repeater is outside the realization rect but still being measured. Also,
-                    // note that if we are measuring this repeater, then we are already realizing an element to figure out the size, so we could
-                    // just keep that element alive. It also helps in XYFocus scenarios to have an element realized for XYFocus to find a candidate
-                    // in the navigating direction.
-                    realizationWindowOffsetInExtent + _orientation.MajorSize(realizationRect) >= 0 && realizationWindowOffsetInExtent <= majorSize)
-                {
-                    anchorIndex = (int) (realizationWindowOffsetInExtent / averageElementSize);
-                    anchorIndex = Math.Max(0, Math.Min(itemsCount - 1, anchorIndex));
-                    offset = anchorIndex* averageElementSize + _orientation.MajorStart(lastExtent);
-                }
-        }
-
-            return new FlowLayoutAnchorInfo { Index = anchorIndex, Offset = offset, };
-        }
-
-        protected internal override void InitializeForContextCore(VirtualizingLayoutContext context)
-        {
-            var state = context.LayoutState;
-            var stackState = state as StackLayoutState;
-            
-            if (stackState == null)
-            {
-                if (state != null)
-                {
-                    throw new InvalidOperationException("LayoutState must derive from StackLayoutState.");
-                }
-
-                // Custom deriving layouts could potentially be stateful.
-                // If that is the case, we will just create the base state required by UniformGridLayout ourselves.
-                stackState = new StackLayoutState();
-            }
-
-            stackState.InitializeForContext(context, this);
-        }
-
-        protected internal override void UninitializeForContextCore(VirtualizingLayoutContext context)
-        {
-            var stackState = (StackLayoutState)context.LayoutState!;
-            stackState.UninitializeForContext(context);
-        }
-
-        protected internal override Size MeasureOverride(VirtualizingLayoutContext context, Size availableSize)
-        {
-            ((StackLayoutState)context.LayoutState!).OnMeasureStart();
-
-            var desiredSize = GetFlowAlgorithm(context).Measure(
-                availableSize,
-                context,
-                false,
-                0,
-                Spacing,
-                int.MaxValue,
-                _orientation.ScrollOrientation,
-                DisableVirtualization,
-                LayoutId);
-
-            return new Size(desiredSize.Width, desiredSize.Height);
-        }
-
-        protected internal override Size ArrangeOverride(VirtualizingLayoutContext context, Size finalSize)
-        {
-            var value = GetFlowAlgorithm(context).Arrange(
-               finalSize,
-               context,
-               false,
-               FlowLayoutAlgorithm.LineAlignment.Start,
-               LayoutId);
-
-            return new Size(value.Width, value.Height);
-        }
-
-        protected internal override void OnItemsChangedCore(VirtualizingLayoutContext context, object? source, NotifyCollectionChangedEventArgs args)
-        {
-            GetFlowAlgorithm(context).OnItemsSourceChanged(source, args, context);
-            // Always invalidate layout to keep the view accurate.
-            InvalidateLayout();
-        }
-
-        protected override void OnPropertyChanged(AvaloniaPropertyChangedEventArgs change)
-        {
-            base.OnPropertyChanged(change);
-            if (change.Property == OrientationProperty)
-            {
-                var orientation = change.GetNewValue<Orientation>();
-
-                //Note: For StackLayout Vertical Orientation means we have a Vertical ScrollOrientation.
-                //Horizontal Orientation means we have a Horizontal ScrollOrientation.
-                _orientation.ScrollOrientation = orientation == Orientation.Horizontal ? ScrollOrientation.Horizontal : ScrollOrientation.Vertical;
-            }
-
-            InvalidateLayout();
-        }
-
-        private static double GetAverageElementSize(
-            Size availableSize,
-            VirtualizingLayoutContext context,
-            StackLayoutState stackLayoutState)
-        {
-            double averageElementSize = 0;
-
-            if (context.ItemCount > 0)
-            {
-                if (stackLayoutState.TotalElementsMeasured == 0)
-                {
-                    var tmpElement = context.GetOrCreateElementAt(0, ElementRealizationOptions.ForceCreate | ElementRealizationOptions.SuppressAutoRecycle);
-                    stackLayoutState.FlowAlgorithm.MeasureElement(tmpElement, 0, availableSize, context);
-                    context.RecycleElement(tmpElement);
-                }
-
-                averageElementSize = Math.Round(stackLayoutState.TotalElementSize / stackLayoutState.TotalElementsMeasured);
-            }
-
-            return averageElementSize;
-        }
-
-        private void InvalidateLayout() => InvalidateMeasure();
-
-        private static FlowLayoutAlgorithm GetFlowAlgorithm(VirtualizingLayoutContext context) => ((StackLayoutState)context.LayoutState!).FlowAlgorithm;
-    }
-}

+ 0 - 61
src/Avalonia.Controls.ItemsRepeater/Layout/StackLayoutState.cs

@@ -1,61 +0,0 @@
-// This source file is adapted from the WinUI project.
-// (https://github.com/microsoft/microsoft-ui-xaml)
-//
-// Licensed to The Avalonia Project under MIT License, courtesy of The .NET Foundation.
-
-using System;
-using System.Collections.Generic;
-using System.Linq;
-
-namespace Avalonia.Layout
-{
-    /// <summary>
-    /// Represents the state of a StackLayout.
-    /// </summary>
-    public class StackLayoutState
-    {
-        private const int BufferSize = 100;
-        private readonly List<double> _estimationBuffer = new List<double>();
-
-        internal FlowLayoutAlgorithm FlowAlgorithm { get; } = new FlowLayoutAlgorithm();
-        internal double MaxArrangeBounds { get; private set; }
-        internal int TotalElementsMeasured { get; private set; }
-        internal double TotalElementSize { get; private set; }
-
-        internal void InitializeForContext(VirtualizingLayoutContext context, IFlowLayoutAlgorithmDelegates callbacks)
-        {
-            FlowAlgorithm.InitializeForContext(context, callbacks);
-
-            if (_estimationBuffer.Count == 0)
-            {
-                _estimationBuffer.AddRange(Enumerable.Repeat(0.0, BufferSize));
-            }
-
-            context.LayoutState = this;
-        }
-
-        internal void UninitializeForContext(VirtualizingLayoutContext context)
-        {
-            FlowAlgorithm.UninitializeForContext(context);
-        }
-
-        internal void OnElementMeasured(int elementIndex, double majorSize, double minorSize)
-        {
-            int estimationBufferIndex = elementIndex % _estimationBuffer.Count;
-            bool alreadyMeasured = _estimationBuffer[estimationBufferIndex] != 0;
-
-            if (!alreadyMeasured)
-            {
-                TotalElementsMeasured++;
-            }
-
-            TotalElementSize -= _estimationBuffer[estimationBufferIndex];
-            TotalElementSize += majorSize;
-            _estimationBuffer[estimationBufferIndex] = majorSize;
-
-            MaxArrangeBounds = Math.Max(MaxArrangeBounds, minorSize);
-        }
-
-        internal void OnMeasureStart() => MaxArrangeBounds = 0;
-    }
-}

+ 0 - 562
src/Avalonia.Controls.ItemsRepeater/Layout/UniformGridLayout.cs

@@ -1,562 +0,0 @@
-// This source file is adapted from the WinUI project.
-// (https://github.com/microsoft/microsoft-ui-xaml)
-//
-// Licensed to The Avalonia Project under MIT License, courtesy of The .NET Foundation.
-
-using System;
-using System.Collections.Specialized;
-using Avalonia.Data;
-using Avalonia.Logging;
-
-namespace Avalonia.Layout
-{
-    /// <summary>
-    /// Defines constants that specify how items are aligned on the non-scrolling or non-virtualizing axis.
-    /// </summary>
-    public enum UniformGridLayoutItemsJustification
-    {
-        /// <summary>
-        /// Items are aligned with the start of the row or column, with extra space at the end.
-        /// Spacing between items does not change.
-        /// </summary>
-        Start = 0,
-
-        /// <summary>
-        /// Items are aligned in the center of the row or column, with extra space at the start and
-        /// end. Spacing between items does not change.
-        /// </summary>
-        Center = 1,
-
-        /// <summary>
-        /// Items are aligned with the end of the row or column, with extra space at the start.
-        /// Spacing between items does not change.
-        /// </summary>
-        End = 2,
-
-        /// <summary>
-        /// Items are aligned so that extra space is added evenly before and after each item.
-        /// </summary>
-        SpaceAround = 3,
-
-        /// <summary>
-        /// Items are aligned so that extra space is added evenly between adjacent items. No space
-        /// is added at the start or end.
-        /// </summary>
-        SpaceBetween = 4,
-
-        SpaceEvenly = 5,
-    };
-
-    /// <summary>
-    /// Defines constants that specify how items are sized to fill the available space.
-    /// </summary>
-    public enum UniformGridLayoutItemsStretch
-    {
-        /// <summary>
-        /// The item retains its natural size. Use of extra space is determined by the
-        /// <see cref="UniformGridLayout.ItemsJustification"/> property.
-        /// </summary>
-        None = 0,
-
-        /// <summary>
-        /// The item is sized to fill the available space in the non-scrolling direction. Item size
-        /// in the scrolling direction is not changed.
-        /// </summary>
-        Fill = 1,
-
-        /// <summary>
-        /// The item is sized to both fill the available space in the non-scrolling direction and
-        /// maintain its aspect ratio.
-        /// </summary>
-        Uniform = 2,
-    };
-
-    /// <summary>
-    /// Positions elements sequentially from left to right or top to bottom in a wrapping layout.
-    /// </summary>
-    public class UniformGridLayout : VirtualizingLayout, IFlowLayoutAlgorithmDelegates
-    {
-        /// <summary>
-        /// Defines the <see cref="ItemsJustification"/> property.
-        /// </summary>
-        public static readonly StyledProperty<UniformGridLayoutItemsJustification> ItemsJustificationProperty =
-            AvaloniaProperty.Register<UniformGridLayout, UniformGridLayoutItemsJustification>(nameof(ItemsJustification));
-
-        /// <summary>
-        /// Defines the <see cref="ItemsStretch"/> property.
-        /// </summary>
-        public static readonly StyledProperty<UniformGridLayoutItemsStretch> ItemsStretchProperty =
-            AvaloniaProperty.Register<UniformGridLayout, UniformGridLayoutItemsStretch>(nameof(ItemsStretch));
-
-        /// <summary>
-        /// Defines the <see cref="MinColumnSpacing"/> property.
-        /// </summary>
-        public static readonly StyledProperty<double> MinColumnSpacingProperty =
-            AvaloniaProperty.Register<UniformGridLayout, double>(nameof(MinColumnSpacing));
-
-        /// <summary>
-        /// Defines the <see cref="MinItemHeight"/> property.
-        /// </summary>
-        public static readonly StyledProperty<double> MinItemHeightProperty =
-            AvaloniaProperty.Register<UniformGridLayout, double>(nameof(MinItemHeight));
-
-        /// <summary>
-        /// Defines the <see cref="MinItemWidth"/> property.
-        /// </summary>
-        public static readonly StyledProperty<double> MinItemWidthProperty =
-            AvaloniaProperty.Register<UniformGridLayout, double>(nameof(MinItemWidth));
-
-        /// <summary>
-        /// Defines the <see cref="MinRowSpacing"/> property.
-        /// </summary>
-        public static readonly StyledProperty<double> MinRowSpacingProperty =
-            AvaloniaProperty.Register<UniformGridLayout, double>(nameof(MinRowSpacing));
-
-        /// <summary>
-        /// Defines the <see cref="MaximumRowsOrColumns"/> property.
-        /// </summary>
-        public static readonly StyledProperty<int> MaximumRowsOrColumnsProperty =
-            AvaloniaProperty.Register<UniformGridLayout, int>(nameof(MaximumRowsOrColumns));
-
-        /// <summary>
-        /// Defines the <see cref="Orientation"/> property.
-        /// </summary>
-        public static readonly StyledProperty<Orientation> OrientationProperty =
-            StackLayout.OrientationProperty.AddOwner<UniformGridLayout>();
-
-        private readonly OrientationBasedMeasures _orientation = new OrientationBasedMeasures();
-        private double _minItemWidth = double.NaN;
-        private double _minItemHeight = double.NaN;
-        private double _minRowSpacing;
-        private double _minColumnSpacing;
-        private UniformGridLayoutItemsJustification _itemsJustification;
-        private UniformGridLayoutItemsStretch _itemsStretch;
-        private int _maximumRowsOrColumns = int.MaxValue;
-
-        /// <summary>
-        /// Initializes a new instance of the <see cref="UniformGridLayout"/> class.
-        /// </summary>
-        public UniformGridLayout()
-        {
-            LayoutId = "UniformGridLayout";
-        } 
-
-        static UniformGridLayout()
-        {
-            OrientationProperty.OverrideDefaultValue<UniformGridLayout>(Orientation.Horizontal);
-        }
-
-        /// <summary>
-        /// Gets or sets a value that indicates how items are aligned on the non-scrolling or non-
-        /// virtualizing axis.
-        /// </summary>
-        /// <value>
-        /// An enumeration value that indicates how items are aligned. The default is Start.
-        /// </value>
-        public UniformGridLayoutItemsJustification ItemsJustification
-        {
-            get => GetValue(ItemsJustificationProperty);
-            set => SetValue(ItemsJustificationProperty, value);
-        }
-
-        /// <summary>
-        /// Gets or sets a value that indicates how items are sized to fill the available space.
-        /// </summary>
-        /// <value>
-        /// An enumeration value that indicates how items are sized to fill the available space.
-        /// The default is None.
-        /// </value>
-        /// <remarks>
-        /// This property enables adaptive layout behavior where the items are sized to fill the
-        /// available space along the non-scrolling axis, and optionally maintain their aspect ratio.
-        /// </remarks>
-        public UniformGridLayoutItemsStretch ItemsStretch
-        {
-            get => GetValue(ItemsStretchProperty);
-            set => SetValue(ItemsStretchProperty, value);
-        }
-
-        /// <summary>
-        /// Gets or sets the minimum space between items on the horizontal axis.
-        /// </summary>
-        /// <remarks>
-        /// The spacing may exceed this minimum value when <see cref="ItemsJustification"/> is set
-        /// to SpaceEvenly, SpaceAround, or SpaceBetween.
-        /// </remarks>
-        public double MinColumnSpacing
-        {
-            get => GetValue(MinColumnSpacingProperty);
-            set => SetValue(MinColumnSpacingProperty, value);
-        }
-
-        /// <summary>
-        /// Gets or sets the minimum height of each item.
-        /// </summary>
-        /// <value>
-        /// The minimum height (in pixels) of each item. The default is NaN, in which case the
-        /// height of the first item is used as the minimum.
-        /// </value>
-        public double MinItemHeight
-        {
-            get => GetValue(MinItemHeightProperty);
-            set => SetValue(MinItemHeightProperty, value);
-        }
-
-        /// <summary>
-        /// Gets or sets the minimum width of each item.
-        /// </summary>
-        /// <value>
-        /// The minimum width (in pixels) of each item. The default is NaN, in which case the width
-        /// of the first item is used as the minimum.
-        /// </value>
-        public double MinItemWidth
-        {
-            get => GetValue(MinItemWidthProperty);
-            set => SetValue(MinItemWidthProperty, value);
-        }
-
-        /// <summary>
-        /// Gets or sets the minimum space between items on the vertical axis.
-        /// </summary>
-        /// <remarks>
-        /// The spacing may exceed this minimum value when <see cref="ItemsJustification"/> is set
-        /// to SpaceEvenly, SpaceAround, or SpaceBetween.
-        /// </remarks>
-        public double MinRowSpacing
-        {
-            get => GetValue(MinRowSpacingProperty);
-            set => SetValue(MinRowSpacingProperty, value);
-        }
-
-        /// <summary>
-        /// Gets or sets the maximum row or column count.
-        /// </summary>
-        public int MaximumRowsOrColumns
-        {
-            get => GetValue(MaximumRowsOrColumnsProperty);
-            set => SetValue(MaximumRowsOrColumnsProperty, value);
-        }
-
-        /// <summary>
-        /// Gets or sets the axis along which items are laid out.
-        /// </summary>
-        /// <value>
-        /// One of the enumeration values that specifies the axis along which items are laid out.
-        /// The default is Vertical.
-        /// </value>
-        public Orientation Orientation
-        {
-            get => GetValue(OrientationProperty);
-            set => SetValue(OrientationProperty, value);
-        }
-
-        internal double LineSpacing => Orientation == Orientation.Horizontal ? _minRowSpacing : _minColumnSpacing;
-        internal double MinItemSpacing => Orientation == Orientation.Horizontal ? _minColumnSpacing : _minRowSpacing;
-
-        Size IFlowLayoutAlgorithmDelegates.Algorithm_GetMeasureSize(
-            int index,
-            Size availableSize,
-            VirtualizingLayoutContext context)
-        {
-            var gridState = (UniformGridLayoutState)context.LayoutState!;
-            return new Size(gridState.EffectiveItemWidth, gridState.EffectiveItemHeight);
-        }
-
-        Size IFlowLayoutAlgorithmDelegates.Algorithm_GetProvisionalArrangeSize(
-            int index,
-            Size measureSize,
-            Size desiredSize,
-            VirtualizingLayoutContext context)
-        {
-            var gridState = (UniformGridLayoutState)context.LayoutState!;
-            return new Size(gridState.EffectiveItemWidth, gridState.EffectiveItemHeight);
-        }
-
-        bool IFlowLayoutAlgorithmDelegates.Algorithm_ShouldBreakLine(int index, double remainingSpace) => remainingSpace < 0;
-
-        FlowLayoutAnchorInfo IFlowLayoutAlgorithmDelegates.Algorithm_GetAnchorForRealizationRect(
-            Size availableSize,
-            VirtualizingLayoutContext context)
-        {
-            Rect bounds = new Rect(double.NaN, double.NaN, double.NaN, double.NaN);
-            int anchorIndex = -1;
-
-            int itemsCount = context.ItemCount;
-            var realizationRect = context.RealizationRect;
-            if (itemsCount > 0 && _orientation.MajorSize(realizationRect) > 0)
-            {
-                var gridState = (UniformGridLayoutState)context.LayoutState!;
-                var lastExtent = gridState.FlowAlgorithm.LastExtent;
-                var itemsPerLine = Math.Min( // note use of unsigned ints
-                    Math.Max(1u, (uint)(_orientation.Minor(availableSize) / GetMinorSizeWithSpacing(context))),
-                    Math.Max(1u, (uint)_maximumRowsOrColumns));
-                var majorSize = (itemsCount / itemsPerLine) * GetMajorSizeWithSpacing(context);
-                var realizationWindowStartWithinExtent = _orientation.MajorStart(realizationRect) - _orientation.MajorStart(lastExtent);
-                if ((realizationWindowStartWithinExtent + _orientation.MajorSize(realizationRect)) >= 0 && realizationWindowStartWithinExtent <= majorSize)
-                {
-                    double offset = Math.Max(0.0, _orientation.MajorStart(realizationRect) - _orientation.MajorStart(lastExtent));
-                    int anchorRowIndex = (int)(offset / GetMajorSizeWithSpacing(context));
-
-                    anchorIndex = (int)Math.Max(0, Math.Min(itemsCount - 1, anchorRowIndex * itemsPerLine));
-                    bounds = GetLayoutRectForDataIndex(availableSize, anchorIndex, lastExtent, context);
-                }
-            }
-
-            return new FlowLayoutAnchorInfo
-            {
-                Index = anchorIndex,
-                Offset = _orientation.MajorStart(bounds)
-            };
-        }
-
-        FlowLayoutAnchorInfo IFlowLayoutAlgorithmDelegates.Algorithm_GetAnchorForTargetElement(
-            int targetIndex,
-            Size availableSize,
-            VirtualizingLayoutContext context)
-        {
-            int index = -1;
-            double offset = double.NaN;
-            int count = context.ItemCount;
-            if (targetIndex >= 0 && targetIndex < count)
-            {
-                int itemsPerLine = (int)Math.Min( // note use of unsigned ints
-                    Math.Max(1u, (uint)(_orientation.Minor(availableSize) / GetMinorSizeWithSpacing(context))),
-                    Math.Max(1u, _maximumRowsOrColumns));
-                int indexOfFirstInLine = (targetIndex / itemsPerLine) * itemsPerLine;
-                index = indexOfFirstInLine;
-                var state = (UniformGridLayoutState)context.LayoutState!;
-                offset = _orientation.MajorStart(GetLayoutRectForDataIndex(availableSize, indexOfFirstInLine, state.FlowAlgorithm.LastExtent, context));
-            }
-
-            return new FlowLayoutAnchorInfo
-            {
-                Index = index,
-                Offset = offset
-            };
-        }
-
-        Rect IFlowLayoutAlgorithmDelegates.Algorithm_GetExtent(
-            Size availableSize,
-            VirtualizingLayoutContext context,
-            Layoutable? firstRealized,
-            int firstRealizedItemIndex,
-            Rect firstRealizedLayoutBounds,
-            Layoutable? lastRealized,
-            int lastRealizedItemIndex,
-            Rect lastRealizedLayoutBounds)
-        {
-            var extent = new Rect();
-
-
-            // Constants
-            int itemsCount = context.ItemCount;
-            double availableSizeMinor = _orientation.Minor(availableSize);
-            int itemsPerLine =
-                (int)Math.Min( // note use of unsigned ints
-                    Math.Max(1u, !double.IsInfinity(availableSizeMinor)
-                        ? (uint)(availableSizeMinor / GetMinorSizeWithSpacing(context))
-                        : (uint)itemsCount),
-                Math.Max(1u, _maximumRowsOrColumns));
-            double lineSize = GetMajorSizeWithSpacing(context);
-
-            if (itemsCount > 0)
-            {
-                _orientation.SetMinorSize(
-                    ref extent,
-                    !double.IsInfinity(availableSizeMinor) && _itemsStretch == UniformGridLayoutItemsStretch.Fill ?
-                    availableSizeMinor :
-                    Math.Max(0.0, itemsPerLine * GetMinorSizeWithSpacing(context) - (double)MinItemSpacing));
-                _orientation.SetMajorSize(
-                    ref extent,
-                    Math.Max(0.0, (itemsCount / itemsPerLine) * lineSize - (double)LineSpacing));
-
-                if (firstRealized != null)
-                {
-                    _orientation.SetMajorStart(
-                        ref extent,
-                        _orientation.MajorStart(firstRealizedLayoutBounds) - (firstRealizedItemIndex / itemsPerLine) * lineSize);
-                    int remainingItems = itemsCount - lastRealizedItemIndex - 1;
-                    _orientation.SetMajorSize(
-                        ref extent,
-                        _orientation.MajorEnd(lastRealizedLayoutBounds) - _orientation.MajorStart(extent) + (remainingItems / itemsPerLine) * lineSize);
-                }
-                else
-                {
-                    Logger.TryGet(LogEventLevel.Verbose, "Repeater")?.Log(this, "{LayoutId}: Estimating extent with no realized elements", LayoutId);
-                }
-            }
-
-            Logger.TryGet(LogEventLevel.Verbose, "Repeater")?.Log(this, "{LayoutId}: Extent is ({Size}). Based on lineSize {LineSize} and items per line {ItemsPerLine}",
-                LayoutId, extent.Size, lineSize, itemsPerLine);
-            return extent;
-        }
-
-        void IFlowLayoutAlgorithmDelegates.Algorithm_OnElementMeasured(Layoutable element, int index, Size availableSize, Size measureSize, Size desiredSize, Size provisionalArrangeSize, VirtualizingLayoutContext context)
-        {
-        }
-
-        void IFlowLayoutAlgorithmDelegates.Algorithm_OnLineArranged(int startIndex, int countInLine, double lineSize, VirtualizingLayoutContext context)
-        {
-        }
-
-        protected internal override void InitializeForContextCore(VirtualizingLayoutContext context)
-        {
-            var state = context.LayoutState;
-            var gridState = state as UniformGridLayoutState;
-            
-            if (gridState == null)
-            {
-                if (state != null)
-                {
-                    throw new InvalidOperationException("LayoutState must derive from UniformGridLayoutState.");
-                }
-
-                // Custom deriving layouts could potentially be stateful.
-                // If that is the case, we will just create the base state required by UniformGridLayout ourselves.
-                gridState = new UniformGridLayoutState();
-            }
-
-            gridState.InitializeForContext(context, this);
-        }
-
-        protected internal override void UninitializeForContextCore(VirtualizingLayoutContext context)
-        {
-            var gridState = (UniformGridLayoutState)context.LayoutState!;
-            gridState.UninitializeForContext(context);
-        }
-
-        protected internal override Size MeasureOverride(VirtualizingLayoutContext context, Size availableSize)
-        {
-            // Set the width and height on the grid state. If the user already set them then use the preset. 
-            // If not, we have to measure the first element and get back a size which we're going to be using for the rest of the items.
-            var gridState = (UniformGridLayoutState)context.LayoutState!;
-            gridState.EnsureElementSize(availableSize, context, _minItemWidth, _minItemHeight, _itemsStretch, Orientation, MinRowSpacing, MinColumnSpacing, _maximumRowsOrColumns);
-
-            var desiredSize = GetFlowAlgorithm(context).Measure(
-                availableSize,
-                context,
-                true,
-                MinItemSpacing,
-                LineSpacing,
-                _maximumRowsOrColumns,
-                _orientation.ScrollOrientation,
-                false,
-                LayoutId);
-
-            // If after Measure the first item is in the realization rect, then we revoke grid state's ownership,
-            // and only use the layout when to clear it when it's done.
-            gridState.EnsureFirstElementOwnership(context);
-
-            return desiredSize;
-        }
-
-        protected internal override Size ArrangeOverride(VirtualizingLayoutContext context, Size finalSize)
-        {
-            var value = GetFlowAlgorithm(context).Arrange(
-               finalSize,
-               context,
-               true,
-               (FlowLayoutAlgorithm.LineAlignment)_itemsJustification,
-               LayoutId);
-            return new Size(value.Width, value.Height);
-        }
-
-        protected internal override void OnItemsChangedCore(VirtualizingLayoutContext context, object? source, NotifyCollectionChangedEventArgs args)
-        {
-            GetFlowAlgorithm(context).OnItemsSourceChanged(source, args, context);
-            // Always invalidate layout to keep the view accurate.
-            InvalidateLayout();
-
-            var gridState = (UniformGridLayoutState)context.LayoutState!;
-            gridState.ClearElementOnDataSourceChange(context, args);
-        }
-
-        protected override void OnPropertyChanged(AvaloniaPropertyChangedEventArgs change)
-        {
-            base.OnPropertyChanged(change);
-            if (change.Property == OrientationProperty)
-            {
-                var orientation = change.GetNewValue<Orientation>();
-
-                //Note: For UniformGridLayout Vertical Orientation means we have a Horizontal ScrollOrientation. Horizontal Orientation means we have a Vertical ScrollOrientation.
-                //i.e. the properties are the inverse of each other.
-                var scrollOrientation = (orientation == Orientation.Horizontal) ? ScrollOrientation.Vertical : ScrollOrientation.Horizontal;
-                _orientation.ScrollOrientation = scrollOrientation;
-            }
-            else if (change.Property == MinColumnSpacingProperty)
-            {
-                _minColumnSpacing = change.GetNewValue<double>();
-            }
-            else if (change.Property == MinRowSpacingProperty)
-            {
-                _minRowSpacing = change.GetNewValue<double>();
-            }
-            else if (change.Property == ItemsJustificationProperty)
-            {
-                _itemsJustification = change.GetNewValue<UniformGridLayoutItemsJustification>();
-            }
-            else if (change.Property == ItemsStretchProperty)
-            {
-                _itemsStretch = change.GetNewValue<UniformGridLayoutItemsStretch>();
-            }
-            else if (change.Property == MinItemWidthProperty)
-            {
-                _minItemWidth = change.GetNewValue<double>();
-            }
-            else if (change.Property == MinItemHeightProperty)
-            {
-                _minItemHeight = change.GetNewValue<double>();
-            }
-            else if (change.Property == MaximumRowsOrColumnsProperty)
-            {
-                _maximumRowsOrColumns = change.GetNewValue<int>();
-            }
-
-            InvalidateLayout();
-        }
-
-        private double GetMinorSizeWithSpacing(VirtualizingLayoutContext context)
-        {
-            var minItemSpacing = MinItemSpacing;
-            var gridState = (UniformGridLayoutState)context.LayoutState!;
-            return _orientation.ScrollOrientation == ScrollOrientation.Vertical?
-                gridState.EffectiveItemWidth + minItemSpacing :
-                gridState.EffectiveItemHeight + minItemSpacing;
-        }
-
-        private double GetMajorSizeWithSpacing(VirtualizingLayoutContext context)
-        {
-            var lineSpacing = LineSpacing;
-            var gridState = (UniformGridLayoutState)context.LayoutState!;
-            return _orientation.ScrollOrientation == ScrollOrientation.Vertical ?
-                gridState.EffectiveItemHeight + lineSpacing :
-                gridState.EffectiveItemWidth + lineSpacing;
-        }
-
-        Rect GetLayoutRectForDataIndex(
-            Size availableSize,
-            int index,
-            Rect lastExtent,
-            VirtualizingLayoutContext context)
-        {
-            int itemsPerLine = (int)Math.Min( //note use of unsigned ints
-                Math.Max(1u, (uint)(_orientation.Minor(availableSize) / GetMinorSizeWithSpacing(context))),
-                Math.Max(1u, _maximumRowsOrColumns));
-            int rowIndex = (int)(index / itemsPerLine);
-            int indexInRow = index - (rowIndex * itemsPerLine);
-
-            var gridState = (UniformGridLayoutState)context.LayoutState!;
-            Rect bounds = _orientation.MinorMajorRect(
-                indexInRow * GetMinorSizeWithSpacing(context) + _orientation.MinorStart(lastExtent),
-                rowIndex * GetMajorSizeWithSpacing(context) + _orientation.MajorStart(lastExtent),
-                _orientation.ScrollOrientation == ScrollOrientation.Vertical ? gridState.EffectiveItemWidth : gridState.EffectiveItemHeight,
-                _orientation.ScrollOrientation == ScrollOrientation.Vertical ? gridState.EffectiveItemHeight : gridState.EffectiveItemWidth);
-
-            return bounds;
-        }
-
-        private void InvalidateLayout() => InvalidateMeasure();
-
-        private static FlowLayoutAlgorithm GetFlowAlgorithm(VirtualizingLayoutContext context) => ((UniformGridLayoutState)context.LayoutState!).FlowAlgorithm;
-    }
-}

+ 0 - 204
src/Avalonia.Controls.ItemsRepeater/Layout/UniformGridLayoutState.cs

@@ -1,204 +0,0 @@
-// This source file is adapted from the WinUI project.
-// (https://github.com/microsoft/microsoft-ui-xaml)
-//
-// Licensed to The Avalonia Project under MIT License, courtesy of The .NET Foundation.
-
-using System;
-using System.Collections.Specialized;
-
-namespace Avalonia.Layout
-{
-    /// <summary>
-    /// Represents the state of a <see cref="UniformGridLayout"/>.
-    /// </summary>
-    public class UniformGridLayoutState
-    {
-        // We need to measure the element at index 0 to know what size to measure all other items. 
-        // If FlowlayoutAlgorithm has already realized element 0 then we can use that. 
-        // If it does not, then we need to do context.GetElement(0) at which point we have requested an element and are on point to clear it.
-        // If we are responsible for clearing element 0 we keep m_cachedFirstElement valid. 
-        // If we are not (because FlowLayoutAlgorithm is holding it for us) then we just null out this field and use the one from FlowLayoutAlgorithm.
-        private Layoutable? _cachedFirstElement;
-
-        internal FlowLayoutAlgorithm FlowAlgorithm { get; } = new FlowLayoutAlgorithm();
-        internal double EffectiveItemWidth { get; private set; }
-        internal double EffectiveItemHeight { get; private set; }
-
-        internal void InitializeForContext(VirtualizingLayoutContext context, IFlowLayoutAlgorithmDelegates callbacks)
-        {
-            FlowAlgorithm.InitializeForContext(context, callbacks);
-            context.LayoutState = this;
-        }
-
-        internal void UninitializeForContext(VirtualizingLayoutContext context)
-        {
-            FlowAlgorithm.UninitializeForContext(context);
-
-            if (_cachedFirstElement != null)
-            {
-                context.RecycleElement(_cachedFirstElement);
-            }
-        }
-
-        internal void EnsureElementSize(
-            Size availableSize,
-            VirtualizingLayoutContext context,
-            double layoutItemWidth,
-            double layoutItemHeight,
-            UniformGridLayoutItemsStretch stretch,
-            Orientation orientation,
-            double minRowSpacing,
-            double minColumnSpacing,
-            int maxItemsPerLine)
-        {
-            if (maxItemsPerLine == 0)
-            {
-                maxItemsPerLine = 1;
-            }
-
-            if (context.ItemCount > 0)
-            {
-                // If the first element is realized we don't need to cache it or to get it from the context
-                var realizedElement = FlowAlgorithm.GetElementIfRealized(0);
-                if (realizedElement != null)
-                {
-                    realizedElement.Measure(availableSize);
-                    SetSize(realizedElement, layoutItemWidth, layoutItemHeight, availableSize, stretch, orientation, minRowSpacing, minColumnSpacing, maxItemsPerLine);
-                    _cachedFirstElement = null;
-                }
-                else
-                {
-                    if (_cachedFirstElement == null)
-                    {
-                        // we only cache if we aren't realizing it
-                        _cachedFirstElement = context.GetOrCreateElementAt(
-                            0,
-                            ElementRealizationOptions.ForceCreate | ElementRealizationOptions.SuppressAutoRecycle); // expensive
-                    }
-
-                    _cachedFirstElement.Measure(availableSize);
-
-                    SetSize(_cachedFirstElement, layoutItemWidth, layoutItemHeight, availableSize, stretch, orientation, minRowSpacing, minColumnSpacing, maxItemsPerLine);
-
-                    // See if we can move ownership to the flow algorithm. If we can, we do not need a local cache.
-                    bool added = FlowAlgorithm.TryAddElement0(_cachedFirstElement);
-                    if (added)
-                    {
-                        _cachedFirstElement = null;
-                    }
-                }
-            }
-        }
-
-        private void SetSize(
-            Layoutable element,
-            double layoutItemWidth,
-            double layoutItemHeight,
-            Size availableSize,
-            UniformGridLayoutItemsStretch stretch,
-            Orientation orientation,
-            double minRowSpacing,
-            double minColumnSpacing,
-            int maxItemsPerLine)
-        {
-            if (maxItemsPerLine == 0)
-            {
-                maxItemsPerLine = 1;
-            }
-
-            EffectiveItemWidth = (double.IsNaN(layoutItemWidth) ? element.DesiredSize.Width : layoutItemWidth);
-            EffectiveItemHeight = (double.IsNaN(layoutItemHeight) ? element.DesiredSize.Height : layoutItemHeight);
-
-            var availableSizeMinor = orientation == Orientation.Horizontal ? availableSize.Width : availableSize.Height;
-            var minorItemSpacing = orientation == Orientation.Vertical ? minRowSpacing : minColumnSpacing;
-
-            var itemSizeMinor = orientation == Orientation.Horizontal ? EffectiveItemWidth : EffectiveItemHeight;
-
-            double extraMinorPixelsForEachItem = 0.0;
-            if (!double.IsInfinity(availableSizeMinor))
-            {
-                var numItemsPerColumn = (int)Math.Min(
-                    maxItemsPerLine,
-                    Math.Max(1.0, availableSizeMinor / (itemSizeMinor + minorItemSpacing)));
-                var usedSpace = (numItemsPerColumn * (itemSizeMinor + minorItemSpacing)) - minorItemSpacing;
-                var remainingSpace = availableSizeMinor - usedSpace;
-                extraMinorPixelsForEachItem = (int)(remainingSpace / numItemsPerColumn);
-            }
-
-            if (stretch == UniformGridLayoutItemsStretch.Fill)
-            {
-                if (orientation == Orientation.Horizontal)
-                {
-                    EffectiveItemWidth += extraMinorPixelsForEachItem;
-                }
-                else
-                {
-                    EffectiveItemHeight += extraMinorPixelsForEachItem;
-                }
-            }
-            else if (stretch == UniformGridLayoutItemsStretch.Uniform)
-            {
-                var itemSizeMajor = orientation == Orientation.Horizontal ? EffectiveItemHeight : EffectiveItemWidth;
-                var extraMajorPixelsForEachItem = itemSizeMajor * (extraMinorPixelsForEachItem / itemSizeMinor);
-                if (orientation == Orientation.Horizontal)
-                {
-                    EffectiveItemWidth += extraMinorPixelsForEachItem;
-                    EffectiveItemHeight += extraMajorPixelsForEachItem;
-                }
-                else
-                {
-                    EffectiveItemHeight += extraMinorPixelsForEachItem;
-                    EffectiveItemWidth += extraMajorPixelsForEachItem;
-                }
-            }
-        }
-
-        internal void EnsureFirstElementOwnership(VirtualizingLayoutContext context)
-        {
-            if (_cachedFirstElement != null && FlowAlgorithm.GetElementIfRealized(0) != null)
-            {
-                // We created the element, but then flowlayout algorithm took ownership, so we can clear it and
-                // let flowlayout algorithm do its thing.
-                context.RecycleElement(_cachedFirstElement);
-                _cachedFirstElement = null;
-            }
-        }
-
-        internal void ClearElementOnDataSourceChange(
-            VirtualizingLayoutContext context,
-            NotifyCollectionChangedEventArgs args)
-        {
-            if (_cachedFirstElement != null)
-            {
-                bool shouldClear = false;
-                switch (args.Action)
-                {
-                case NotifyCollectionChangedAction.Add:
-                    shouldClear = args.NewStartingIndex == 0;
-                    break;
-
-                case NotifyCollectionChangedAction.Replace:
-                    shouldClear = args.NewStartingIndex == 0 || args.OldStartingIndex == 0;
-                    break;
-
-                case NotifyCollectionChangedAction.Remove:
-                    shouldClear = args.OldStartingIndex == 0;
-                    break;
-
-                case NotifyCollectionChangedAction.Reset:
-                    shouldClear = true;
-                    break;
-
-                case NotifyCollectionChangedAction.Move:
-                    throw new NotImplementedException();
-                }
-
-                if (shouldClear)
-                {
-                    context.RecycleElement(_cachedFirstElement);
-                    _cachedFirstElement = null;
-                }
-            }
-        }
-    }
-}

+ 0 - 27
src/Avalonia.Controls.ItemsRepeater/Layout/Utils/ListUtils.cs

@@ -1,27 +0,0 @@
-using System.Collections.Generic;
-using System.Linq;
-
-namespace Avalonia.Layout.Utils
-{
-    internal static class ListUtils
-    {
-        public static void Resize<T>(this List<T> list, int size, T value)
-        {
-            int cur = list.Count;
-
-            if (size < cur)
-            {
-                list.RemoveRange(size, cur - size);
-            }
-            else if (size > cur)
-            {
-                if (size > list.Capacity)
-                {
-                    list.Capacity = size;
-                }
-
-                list.AddRange(Enumerable.Repeat(value, size - cur));
-            }
-        }
-    }
-}

+ 0 - 37
src/Avalonia.Controls.ItemsRepeater/Layout/UvBounds.cs

@@ -1,37 +0,0 @@
-// Licensed to the .NET Foundation under one or more agreements.
-// The .NET Foundation licenses this file to you under the MIT license.
-// See the LICENSE file in the project root for more information.
-using Avalonia;
-using Avalonia.Layout;
-
-namespace Avalonia.Layout
-{
-    internal struct UvBounds
-    {
-        public UvBounds(Orientation orientation, Rect rect)
-        {
-            if (orientation == Orientation.Horizontal)
-            {
-                UMin = rect.Left;
-                UMax = rect.Right;
-                VMin = rect.Top;
-                VMax = rect.Bottom;
-            }
-            else
-            {
-                UMin = rect.Top;
-                UMax = rect.Bottom;
-                VMin = rect.Left;
-                VMax = rect.Right;
-            }
-        }
-
-        public double UMin { get; }
-
-        public double UMax { get; }
-
-        public double VMin { get; }
-
-        public double VMax { get; }
-    }
-}

+ 0 - 45
src/Avalonia.Controls.ItemsRepeater/Layout/UvMeasure.cs

@@ -1,45 +0,0 @@
-// Licensed to the .NET Foundation under one or more agreements.
-// The .NET Foundation licenses this file to you under the MIT license.
-// See the LICENSE file in the project root for more information.
-using Avalonia.Layout;
-
-namespace Avalonia.Layout
-{    
-    internal struct UvMeasure
-    {
-        internal static readonly UvMeasure Zero = default;
-
-        internal double U { get; set; }
-
-        internal double V { get; set; }
-
-        public UvMeasure(Orientation orientation, double width, double height)
-        {
-            if (orientation == Orientation.Horizontal)
-            {
-                U = width;
-                V = height;
-            }
-            else
-            {
-                U = height;
-                V = width;
-            }
-        }
-
-        public override bool Equals(object? obj)
-        {
-            if (obj is UvMeasure measure)
-            {
-                return (measure.U == U) && (measure.V == V);
-            }
-
-            return false;
-        }
-
-        public override int GetHashCode()
-        {
-            return base.GetHashCode();
-        }
-    }
-}

+ 0 - 42
src/Avalonia.Controls.ItemsRepeater/Layout/VirtualLayoutContextAdapter.cs

@@ -1,42 +0,0 @@
-using System.Collections;
-using System.Collections.Generic;
-
-namespace Avalonia.Layout
-{
-    public class VirtualLayoutContextAdapter : NonVirtualizingLayoutContext
-    {
-        private readonly VirtualizingLayoutContext _virtualizingContext;
-        private ChildrenCollection? _children;
-
-        public VirtualLayoutContextAdapter(VirtualizingLayoutContext virtualizingContext)
-        {
-            _virtualizingContext = virtualizingContext;
-        }
-
-        protected override object? LayoutStateCore
-        {
-            get => _virtualizingContext.LayoutState;
-            set => _virtualizingContext.LayoutState = value;
-        }
-
-        protected override IReadOnlyList<Layoutable> ChildrenCore =>
-            _children ?? (_children = new ChildrenCollection(_virtualizingContext));
-
-        private class ChildrenCollection : IReadOnlyList<Layoutable>
-        {
-            private readonly VirtualizingLayoutContext _context;
-            public ChildrenCollection(VirtualizingLayoutContext context) => _context = context;
-            public Layoutable this[int index] => _context.GetOrCreateElementAt(index);
-            public int Count => _context.ItemCount;
-            IEnumerator IEnumerable.GetEnumerator() => GetEnumerator();
-
-            public IEnumerator<Layoutable> GetEnumerator()
-            {
-                for (var i = 0; i < Count; ++i)
-                {
-                    yield return this[i];
-                }
-            }
-        }
-    }
-}

+ 0 - 119
src/Avalonia.Controls.ItemsRepeater/Layout/VirtualizingLayout.cs

@@ -1,119 +0,0 @@
-// This source file is adapted from the WinUI project.
-// (https://github.com/microsoft/microsoft-ui-xaml)
-//
-// Licensed to The Avalonia Project under MIT License, courtesy of The .NET Foundation.
-
-using System.Collections.Specialized;
-
-namespace Avalonia.Layout
-{
-    /// <summary>
-    /// Represents the base class for an object that sizes and arranges child elements for a host
-    /// and supports virtualization.
-    /// </summary>
-    /// <remarks>
-    /// <see cref="VirtualizingLayout"/> is the base class for layouts that support virtualization.
-    /// You can use one of the provided derived class, or inherit from it to create your own layout.
-    /// Provided concrete virtualizing layout classes are <see cref="StackLayout"/> and 
-    /// <see cref="UniformGridLayout"/>.
-    /// </remarks>
-    public abstract class VirtualizingLayout : AttachedLayout
-    {
-        /// <summary>
-        /// Notifies the layout when the data collection assigned to the container element (Items)
-        /// has changed.
-        /// </summary>
-        /// <param name="context">
-        /// The context object that facilitates communication between the layout and its host
-        /// container.
-        /// </param>
-        /// <param name="source">The data source.</param>
-        /// <param name="args">Data about the collection change.</param>
-        /// <remarks>
-        /// Override <see cref="OnItemsChangedCore(VirtualizingLayoutContext, object, NotifyCollectionChangedEventArgs)"/>
-        /// to provide the behavior for this method in a derived class.
-        /// </remarks>
-        public void OnItemsChanged(
-            VirtualizingLayoutContext context,
-            object? source,
-            NotifyCollectionChangedEventArgs args) => OnItemsChangedCore(context, source, args);
-
-        /// <summary>
-        /// When overridden in a derived class, initializes any per-container state the layout
-        /// requires when it is attached to an ILayoutable container.
-        /// </summary>
-        /// <param name="context">
-        /// The context object that facilitates communication between the layout and its host
-        /// container.
-        /// </param>
-        protected internal virtual void InitializeForContextCore(VirtualizingLayoutContext context)
-        {
-        }
-
-        /// <summary>
-        /// When overridden in a derived class, removes any state the layout previously stored on
-        /// the ILayoutable container.
-        /// </summary>
-        /// <param name="context">
-        /// The context object that facilitates communication between the layout and its host
-        /// container.
-        /// </param>
-        protected internal virtual void UninitializeForContextCore(VirtualizingLayoutContext context)
-        {
-        }
-
-        /// <summary>
-        /// Provides the behavior for the "Measure" pass of the layout cycle. Classes can override
-        /// this method to define their own "Measure" pass behavior.
-        /// </summary>
-        /// <param name="context">
-        /// The context object that facilitates communication between the layout and its host
-        /// container.
-        /// </param>
-        /// <param name="availableSize">
-        /// The available size that this object can give to child objects. Infinity can be
-        /// specified as a value to indicate that the object will size to whatever content is
-        /// available.
-        /// </param>
-        /// <returns>
-        /// The size that this object determines it needs during layout, based on its calculations
-        /// of the allocated sizes for child objects or based on other considerations such as a
-        /// fixed container size.
-        /// </returns>
-        protected internal abstract Size MeasureOverride(
-            VirtualizingLayoutContext context,
-            Size availableSize);
-
-        /// <summary>
-        /// When implemented in a derived class, provides the behavior for the "Arrange" pass of
-        /// layout. Classes can override this method to define their own "Arrange" pass behavior.
-        /// </summary>
-        /// <param name="context">
-        /// The context object that facilitates communication between the layout and its host
-        /// container.
-        /// </param>
-        /// <param name="finalSize">
-        /// The final area within the container that this object should use to arrange itself and
-        /// its children.
-        /// </param>
-        /// <returns>The actual size that is used after the element is arranged in layout.</returns>
-        protected internal virtual Size ArrangeOverride(
-            VirtualizingLayoutContext context,
-            Size finalSize) => finalSize;
-
-        /// <summary>
-        /// Notifies the layout when the data collection assigned to the container element (Items)
-        /// has changed.
-        /// </summary>
-        /// <param name="context">
-        /// The context object that facilitates communication between the layout and its host
-        /// container.
-        /// </param>
-        /// <param name="source">The data source.</param>
-        /// <param name="args">Data about the collection change.</param>
-        protected internal virtual void OnItemsChangedCore(
-            VirtualizingLayoutContext context,
-            object? source,
-            NotifyCollectionChangedEventArgs args) => InvalidateMeasure();
-    }
-}

+ 0 - 195
src/Avalonia.Controls.ItemsRepeater/Layout/VirtualizingLayoutContext.cs

@@ -1,195 +0,0 @@
-// This source file is adapted from the WinUI project.
-// (https://github.com/microsoft/microsoft-ui-xaml)
-//
-// Licensed to The Avalonia Project under MIT License, courtesy of The .NET Foundation.
-
-using System;
-
-namespace Avalonia.Layout
-{
-    /// <summary>
-    /// Defines constants that specify whether to suppress automatic recycling of the retrieved
-    /// element or force creation of a new element.
-    /// </summary>
-    /// <remarks>
-    /// When you call <see cref="VirtualizingLayoutContext.GetOrCreateElementAt(int, ElementRealizationOptions)"/>,
-    /// you can specify whether to suppress automatic recycling of the retrieved element or force
-    /// creation of a new element. Elements retrieved with automatic recycling suppressed
-    /// (SuppressAutoRecycle) are ignored by the automatic recycling logic that clears realized
-    /// elements that were not retrieved as part of the current layout pass. You must explicitly
-    /// recycle these elements by passing them to the RecycleElement method to avoid memory leaks.
-    /// </remarks>
-    [Flags]
-    public enum ElementRealizationOptions
-    {
-        /// <summary>
-        /// No option is specified.
-        /// </summary>
-        None = 0x0,
-
-        /// <summary>
-        /// Creation of a new element is forced.
-        /// </summary>
-        ForceCreate = 0x1,
-
-        /// <summary>
-        /// The element is ignored by the automatic recycling logic.
-        /// </summary>
-        SuppressAutoRecycle = 0x2,
-    };
-
-    /// <summary>
-    /// Represents the base class for layout context types that support virtualization.
-    /// </summary>
-    public abstract class VirtualizingLayoutContext : LayoutContext
-    {
-        private NonVirtualizingLayoutContext? _contextAdapter;
-
-        /// <summary>
-        /// Gets the number of items in the data.
-        /// </summary>
-        /// <remarks>
-        /// This property gets the value returned by ItemCountCore, which must be implemented in
-        /// a derived class.
-        /// </remarks>
-        public int ItemCount => ItemCountCore();
-
-        /// <summary>
-        /// Gets or sets the origin point for the estimated content size.
-        /// </summary>
-        /// <remarks>
-        /// LayoutOrigin is used by virtualizing layouts that rely on estimations when determining
-        /// the size and position of content. It allows the layout to fix-up the estimated origin
-        /// of the content as it changes due to on-going estimation or potentially identifying the
-        /// actual size to use. For example, it’s possible that as a user is scrolling back to the
-        /// top of the content that the layout's estimates for the content size that it reports as
-        /// part of its MeasureOverride become increasingly accurate. If the predicted position of
-        /// the content does not already match the previously predicted position (for example, if
-        /// the size of the elements ends up being smaller than previously thought), then the
-        /// layout can indicate a new origin. The viewport provided to the layout on subsequent
-        /// passes will take into account the adjusted origin.
-        /// </remarks>
-        public Point LayoutOrigin { get => LayoutOriginCore; set => LayoutOriginCore = value; }
-
-        /// <summary>
-        /// Gets an area that represents the viewport and buffer that the layout should fill with
-        /// realized elements.
-        /// </summary>
-        public Rect RealizationRect => RealizationRectCore();
-
-        /// <summary>
-        /// Gets the recommended index from which to start the generation and layout of elements.
-        /// </summary>
-        /// <remarks>
-        /// The recommended index might be the result of programmatically realizing an element and
-        /// requesting that it be brought into view. Or, it may be that a user drags the scrollbar
-        /// thumb so quickly that the new viewport and the viewport and buffer previously given to
-        /// the layout do not intersect, so a new index is suggested as the anchor from which to
-        /// generate and layout other elements.
-        /// </remarks>
-        public int RecommendedAnchorIndex => RecommendedAnchorIndexCore;
-
-        /// <summary>
-        /// Implements the behavior of LayoutOrigin in a derived or custom VirtualizingLayoutContext.
-        /// </summary>
-        protected abstract Point LayoutOriginCore { get; set; }
-
-        /// <summary>
-        /// Implements the behavior for getting the return value of RecommendedAnchorIndex in a
-        /// derived or custom <see cref="VirtualizingLayoutContext"/>.
-        /// </summary>
-        protected virtual int RecommendedAnchorIndexCore { get; }
-
-        /// <summary>
-        /// Retrieves the data item in the source found at the specified index.
-        /// </summary>
-        /// <param name="index">The index of the data item to retrieve.</param>
-        public object GetItemAt(int index) => GetItemAtCore(index);
-
-        /// <summary>
-        /// Retrieves a UIElement that represents the data item in the source found at the
-        /// specified index. By default, if an element already exists, it is returned; otherwise,
-        /// a new element is created.
-        /// </summary>
-        /// <param name="index">The index of the data item to retrieve a UIElement for.</param>
-        /// <remarks>
-        /// This method calls <see cref="GetOrCreateElementAtCore(int, ElementRealizationOptions)"/>
-        /// with options set to None. GetElementAtCore must be implemented in a derived class.
-        /// </remarks>
-        public Layoutable GetOrCreateElementAt(int index)
-            => GetOrCreateElementAtCore(index, ElementRealizationOptions.None);
-
-        /// <summary>
-        /// Retrieves a UIElement that represents the data item in the source found at the
-        /// specified index using the specified options.
-        /// </summary>
-        /// <param name="index">The index of the data item to retrieve a UIElement for.</param>
-        /// <param name="options">
-        /// A value of <see cref="ElementRealizationOptions"/> that specifies whether to suppress
-        /// automatic recycling of the retrieved element or force creation of a new element.
-        /// </param>
-        /// <remarks>
-        /// This method calls <see cref="GetOrCreateElementAtCore(int, ElementRealizationOptions)"/>,
-        /// which must be implemented in a derived class. When you request an element for the
-        /// specified index, you can optionally specify whether to suppress automatic recycling of
-        /// the retrieved element or force creation of a new element.Elements retrieved with
-        /// automatic recycling suppressed(SuppressAutoRecycle) are ignored by the automatic
-        /// recycling logic that clears realized elements that were not retrieved as part of the
-        /// current layout pass.You must explicitly recycle these elements by passing them to the
-        /// RecycleElement method to avoid memory leaks. These options are intended for more
-        /// advanced layouts that choose to explicitly manage the realization and recycling of
-        /// elements as a performance optimization.
-        /// </remarks>
-        public Layoutable GetOrCreateElementAt(int index, ElementRealizationOptions options)
-            => GetOrCreateElementAtCore(index, options);
-
-        /// <summary>
-        /// Clears the specified UIElement and allows it to be either re-used or released.
-        /// </summary>
-        /// <param name="element">The element to clear.</param>
-        /// <remarks>
-        /// This method calls <see cref="RecycleElementCore(Layoutable)"/>, which must be implemented
-        /// in a derived class.
-        /// </remarks>
-        public void RecycleElement(Layoutable element) => RecycleElementCore(element);
-
-        /// <summary>
-        /// When implemented in a derived class, retrieves the number of items in the data.
-        /// </summary>
-        protected abstract int ItemCountCore();
-
-        /// <summary>
-        /// When implemented in a derived class, retrieves the data item in the source found at the
-        /// specified index.
-        /// </summary>
-        /// <param name="index">The index of the data item to retrieve.</param>
-        protected abstract object GetItemAtCore(int index);
-
-        /// <summary>
-        /// When implemented in a derived class, retrieves an area that represents the viewport and
-        /// buffer that the layout should fill with realized elements.
-        /// </summary>
-        protected abstract Rect RealizationRectCore();
-
-        /// <summary>
-        /// When implemented in a derived class, retrieves a UIElement that represents the data item
-        /// in the source found at the specified index using the specified options.
-        /// </summary>
-        /// <param name="index">The index of the data item to retrieve a UIElement for.</param>
-        /// <param name="options">
-        /// A value of <see cref="ElementRealizationOptions"/> that specifies whether to suppress
-        /// automatic recycling of the retrieved element or force creation of a new element.
-        /// </param>
-        protected abstract Layoutable GetOrCreateElementAtCore(int index, ElementRealizationOptions options);
-
-        /// <summary>
-        /// When implemented in a derived class, clears the specified UIElement and allows it to be
-        /// either re-used or released.
-        /// </summary>
-        /// <param name="element">The element to clear.</param>
-        protected abstract void RecycleElementCore(Layoutable element);
-
-        internal NonVirtualizingLayoutContext GetNonVirtualizingContextAdapter() =>
-            _contextAdapter ?? (_contextAdapter = new VirtualLayoutContextAdapter(this));
-    }
-}

+ 0 - 25
src/Avalonia.Controls.ItemsRepeater/Layout/WrapItem.cs

@@ -1,25 +0,0 @@
-// Licensed to the .NET Foundation under one or more agreements.
-// The .NET Foundation licenses this file to you under the MIT license.
-// See the LICENSE file in the project root for more information.
-
-using Avalonia.Controls;
-using Avalonia.Layout;
-
-namespace Avalonia.Layout
-{
-    internal class WrapItem
-    {
-        public WrapItem(int index)
-        {
-            this.Index = index;
-        }
-
-        public int Index { get; }
-
-        public UvMeasure? Measure { get; internal set; }
-
-        public UvMeasure? Position { get; internal set; }
-
-        public Layoutable? Element { get; internal set; }
-    }
-}

+ 0 - 336
src/Avalonia.Controls.ItemsRepeater/Layout/WrapLayout.cs

@@ -1,336 +0,0 @@
-// Licensed to the .NET Foundation under one or more agreements.
-// The .NET Foundation licenses this file to you under the MIT license.
-// See the LICENSE file in the project root for more information.
-
-using Avalonia;
-using Avalonia.Controls;
-using Avalonia.Data;
-using Avalonia.Layout;
-using System;
-using System.Collections.Specialized;
-
-namespace Avalonia.Layout
-{
-    /// <summary>
-    /// Arranges elements by wrapping them to fit the available space.
-    /// When <see cref="Orientation"/> is set to Orientation.Horizontal, element are arranged in rows until the available width is reached and then to a new row.
-    /// When <see cref="Orientation"/> is set to Orientation.Vertical, element are arranged in columns until the available height is reached.
-    /// </summary>
-    public class WrapLayout : VirtualizingLayout
-    {
-        /// <summary>
-        /// Gets or sets a uniform Horizontal distance (in pixels) between items when <see cref="Orientation"/> is set to Horizontal,
-        /// or between columns of items when <see cref="Orientation"/> is set to Vertical.
-        /// </summary>
-        public double HorizontalSpacing
-        {
-            get { return (double)GetValue(HorizontalSpacingProperty); }
-            set { SetValue(HorizontalSpacingProperty, value); }
-        }
-
-        /// <summary>
-        /// Identifies the <see cref="HorizontalSpacing"/> dependency property.
-        /// </summary>
-        public static readonly StyledProperty<double> HorizontalSpacingProperty =
-            AvaloniaProperty.Register<WrapLayout, double>(nameof(HorizontalSpacing), 0);
-
-        /// <summary>
-        /// Gets or sets a uniform Vertical distance (in pixels) between items when <see cref="Orientation"/> is set to Vertical,
-        /// or between rows of items when <see cref="Orientation"/> is set to Horizontal.
-        /// </summary>
-        public double VerticalSpacing
-        {
-            get { return (double)GetValue(VerticalSpacingProperty); }
-            set { SetValue(VerticalSpacingProperty, value); }
-        }
-
-        /// <summary>
-        /// Identifies the <see cref="VerticalSpacing"/> dependency property.
-        /// </summary>
-        public static readonly StyledProperty<double> VerticalSpacingProperty =
-            AvaloniaProperty.Register<WrapLayout, double>(
-                nameof(VerticalSpacing), 0d);
-
-        /// <summary>
-        /// Gets or sets the orientation of the WrapLayout.
-        /// Horizontal means that child controls will be added horizontally until the width of the panel is reached, then a new row is added to add new child controls.
-        /// Vertical means that children will be added vertically until the height of the panel is reached, then a new column is added.
-        /// </summary>
-        public Orientation Orientation
-        {
-            get { return (Orientation)GetValue(OrientationProperty); }
-            set { SetValue(OrientationProperty, value); }
-        }
-
-        /// <summary>
-        /// Identifies the <see cref="Orientation"/> dependency property.
-        /// </summary>
-        public static readonly StyledProperty<Orientation> OrientationProperty =
-            AvaloniaProperty.Register<WrapLayout, Orientation>(
-                nameof(Orientation),
-                Orientation.Horizontal);
-
-        /// <inheritdoc />
-        protected internal override void InitializeForContextCore(VirtualizingLayoutContext context)
-        {
-            var state = new WrapLayoutState(context);
-            context.LayoutState = state;
-            base.InitializeForContextCore(context);
-        }
-
-        /// <inheritdoc />
-        protected internal override void UninitializeForContextCore(VirtualizingLayoutContext context)
-        {
-            context.LayoutState = null;
-            base.UninitializeForContextCore(context);
-        }
-
-        /// <inheritdoc />
-        protected internal override void OnItemsChangedCore(VirtualizingLayoutContext context, object? source, NotifyCollectionChangedEventArgs args)
-        {
-            var state = (WrapLayoutState)context.LayoutState!;
-
-            switch (args.Action)
-            {
-                case NotifyCollectionChangedAction.Add:
-                    state.RemoveFromIndex(args.NewStartingIndex);
-                    break;
-                case NotifyCollectionChangedAction.Move:
-                    int minIndex = Math.Min(args.NewStartingIndex, args.OldStartingIndex);
-                    state.RemoveFromIndex(minIndex);
-
-                    state.RecycleElementAt(args.OldStartingIndex);
-                    state.RecycleElementAt(args.NewStartingIndex);
-                    break;
-                case NotifyCollectionChangedAction.Remove:
-                    state.RemoveFromIndex(args.OldStartingIndex);
-                    break;
-                case NotifyCollectionChangedAction.Replace:
-                    state.RemoveFromIndex(args.NewStartingIndex);
-                    state.RecycleElementAt(args.NewStartingIndex);
-                    break;
-                case NotifyCollectionChangedAction.Reset:
-                    state.Clear();
-                    break;
-            }
-
-            base.OnItemsChangedCore(context, source, args);
-        }
-
-        /// <inheritdoc />
-        protected internal override Size MeasureOverride(VirtualizingLayoutContext context, Size availableSize)
-        {
-            var totalMeasure = UvMeasure.Zero;
-            var parentMeasure = new UvMeasure(Orientation, availableSize.Width, availableSize.Height);
-            var spacingMeasure = new UvMeasure(Orientation, HorizontalSpacing, VerticalSpacing);
-            var realizationBounds = new UvBounds(Orientation, context.RealizationRect);
-            var position = UvMeasure.Zero;
-
-            var state = (WrapLayoutState)context.LayoutState!;
-            if (state.Orientation != Orientation)
-            {
-                state.SetOrientation(Orientation);
-            }
-
-            if (spacingMeasure.Equals(state.Spacing) == false)
-            {
-                state.ClearPositions();
-                state.Spacing = spacingMeasure;
-            }
-
-            if (state.AvailableU != parentMeasure.U)
-            {
-                state.ClearPositions();
-                state.AvailableU = parentMeasure.U;
-            }
-
-            double currentV = 0;
-            for (int i = 0; i < context.ItemCount; i++)
-            {
-                bool measured = false;
-                WrapItem item = state.GetItemAt(i);
-                if (item.Measure == null)
-                {
-                    item.Element = context.GetOrCreateElementAt(i);
-                    item.Element.Measure(availableSize);
-                    item.Measure = new UvMeasure(Orientation, item.Element.DesiredSize.Width, item.Element.DesiredSize.Height);
-                    measured = true;
-                }
-
-                UvMeasure currentMeasure = item.Measure.Value;
-                if (currentMeasure.U == 0)
-                {
-                    continue; // ignore collapsed items
-                }
-
-                if (item.Position == null)
-                {
-                    if (parentMeasure.U < position.U + currentMeasure.U)
-                    {
-                        // New Row
-                        position.U = 0;
-                        position.V += currentV + spacingMeasure.V;
-                        currentV = 0;
-                    }
-
-                    item.Position = position;
-                }
-
-                position = item.Position.Value;
-
-                double vEnd = position.V + currentMeasure.V;
-                if (vEnd < realizationBounds.VMin)
-                {
-                    // Item is "above" the bounds
-                    if (item.Element != null)
-                    {
-                        context.RecycleElement(item.Element);
-                        item.Element = null;
-                    }
-                }
-                else if (position.V > realizationBounds.VMax)
-                {
-                    // Item is "below" the bounds.
-                    if (item.Element != null)
-                    {
-                        context.RecycleElement(item.Element);
-                        item.Element = null;
-                    }
-
-                    // We don't need to measure anything below the bounds
-                    break;
-                }
-                else if (measured == false)
-                {
-                    // Always measure elements that are within the bounds
-                    item.Element = context.GetOrCreateElementAt(i);
-                    item.Element.Measure(availableSize);
-
-                    currentMeasure = new UvMeasure(Orientation, item.Element.DesiredSize.Width, item.Element.DesiredSize.Height);
-                    if (currentMeasure.Equals(item.Measure) == false)
-                    {
-                        // this item changed size; we need to recalculate layout for everything after this
-                        state.RemoveFromIndex(i + 1);
-                        item.Measure = currentMeasure;
-
-                        // did the change make it go into the new row?
-                        if (parentMeasure.U < position.U + currentMeasure.U)
-                        {
-                            // New Row
-                            position.U = 0;
-                            position.V += currentV + spacingMeasure.V;
-                            currentV = 0;
-                        }
-
-                        item.Position = position;
-                    }
-                }
-
-                position.U += currentMeasure.U + spacingMeasure.U;
-                currentV = Math.Max(currentMeasure.V, currentV);
-            }
-
-            // update value with the last line
-            // if the the last loop is(parentMeasure.U > currentMeasure.U + lineMeasure.U) the total isn't calculated then calculate it
-            // if the last loop is (parentMeasure.U > currentMeasure.U) the currentMeasure isn't added to the total so add it here
-            // for the last condition it is zeros so adding it will make no difference
-            // this way is faster than an if condition in every loop for checking the last item
-            totalMeasure.U = parentMeasure.U;
-            
-            // Propagating an infinite size causes a crash. This can happen if the parent is scrollable and infinite in the opposite
-            // axis to the panel. Clearing to zero prevents the crash.
-            // This is likely an incorrect use of the control by the developer, however we need stability here so setting a default that wont crash.
-            if (double.IsInfinity(totalMeasure.U))
-            {
-                totalMeasure.U = 0.0;
-            }
-            
-            totalMeasure.V = state.GetHeight();
-
-            totalMeasure.U = Math.Ceiling(totalMeasure.U);
-
-            return Orientation == Orientation.Horizontal ? new Size(totalMeasure.U, totalMeasure.V) : new Size(totalMeasure.V, totalMeasure.U);
-        }
-
-        /// <inheritdoc />
-        protected internal override Size ArrangeOverride(VirtualizingLayoutContext context, Size finalSize)
-        {
-            if (context.ItemCount > 0)
-            {
-                var parentMeasure = new UvMeasure(Orientation, finalSize.Width, finalSize.Height);
-                var spacingMeasure = new UvMeasure(Orientation, HorizontalSpacing, VerticalSpacing);
-                var realizationBounds = new UvBounds(Orientation, context.RealizationRect);
-
-                var state = (WrapLayoutState)context.LayoutState!;
-                bool Arrange(WrapItem item, bool isLast = false)
-                {
-                    if (item.Measure.HasValue == false)
-                    {
-                        return false;
-                    }
-
-                    if (item.Position == null)
-                    {
-                        return false;
-                    }
-
-                    var desiredMeasure = item.Measure.Value;
-                    if (desiredMeasure.U == 0)
-                    {
-                        return true; // if an item is collapsed, avoid adding the spacing
-                    }
-
-                    UvMeasure position = item.Position.Value;
-
-                    // Stretch the last item to fill the available space
-                    if (isLast)
-                    {
-                        desiredMeasure.U = parentMeasure.U - position.U;
-                    }
-
-                    if (((position.V + desiredMeasure.V) >= realizationBounds.VMin) && (position.V <= realizationBounds.VMax))
-                    {
-                        // place the item
-                        var child = context.GetOrCreateElementAt(item.Index);
-                        if (Orientation == Orientation.Horizontal)
-                        {
-                            child.Arrange(new Rect(position.U, position.V, desiredMeasure.U, desiredMeasure.V));
-                        }
-                        else
-                        {
-                            child.Arrange(new Rect(position.V, position.U, desiredMeasure.V, desiredMeasure.U));
-                        }
-                    }
-                    else if (position.V > realizationBounds.VMax)
-                    {
-                        return false;
-                    }
-
-                    return true;
-                }
-
-                for (var i = 0; i < context.ItemCount; i++)
-                {
-                    bool continueArranging = Arrange(state.GetItemAt(i));
-                    if (continueArranging == false)
-                    {
-                        break;
-                    }
-                }
-            }
-
-            return finalSize;
-        }
-
-        protected override void OnPropertyChanged(AvaloniaPropertyChangedEventArgs change)
-        {
-            base.OnPropertyChanged(change);
-
-            if (change.Property == OrientationProperty || change.Property == HorizontalSpacingProperty || change.Property == VerticalSpacingProperty)
-            {
-                InvalidateMeasure();
-                InvalidateArrange();
-            }
-        }
-    }
-}

+ 0 - 146
src/Avalonia.Controls.ItemsRepeater/Layout/WrapLayoutState.cs

@@ -1,146 +0,0 @@
-// Licensed to the .NET Foundation under one or more agreements.
-// The .NET Foundation licenses this file to you under the MIT license.
-// See the LICENSE file in the project root for more information.
-
-using System;
-using System.Collections.Generic;
-using System.Linq;
-
-namespace Avalonia.Layout
-{
-    internal class WrapLayoutState
-    {
-        private List<WrapItem> _items = new List<WrapItem>();
-        private VirtualizingLayoutContext _context;
-
-        public WrapLayoutState(VirtualizingLayoutContext context)
-        {
-            this._context = context;
-        }
-
-        public Orientation Orientation { get; private set; }
-
-        public UvMeasure Spacing { get; internal set; }
-
-        public double AvailableU { get; internal set; }
-
-        internal WrapItem GetItemAt(int index)
-        {
-            if (index < 0)
-            {
-                throw new IndexOutOfRangeException();
-            }
-
-            if (index <= (_items.Count - 1))
-            {
-                return _items[index];
-            }
-            else
-            {
-                WrapItem item = new WrapItem(index);
-                _items.Add(item);
-                return item;
-            }
-        }
-
-        internal void Clear()
-        {
-            _items.Clear();
-        }
-
-        internal void RemoveFromIndex(int index)
-        {
-            if (index >= _items.Count)
-            {
-                // Item was added/removed but we haven't realized that far yet
-                return;
-            }
-
-            int numToRemove = _items.Count - index;
-            _items.RemoveRange(index, numToRemove);
-        }
-
-        internal void SetOrientation(Orientation orientation)
-        {
-            foreach (var item in _items)
-            {
-                if (!item.Measure.HasValue)
-                    continue;
-
-                UvMeasure measure = item.Measure.Value;
-                double v = measure.V;
-                measure.V = measure.U;
-                measure.U = v;
-                item.Measure = measure;
-                item.Position = null;
-            }
-
-            Orientation = orientation;
-            AvailableU = 0;
-        }
-
-        internal void ClearPositions()
-        {
-            foreach (var item in _items)
-            {
-                item.Position = null;
-            }
-        }
-
-        internal double GetHeight()
-        {
-            if (_items.Count == 0)
-            {
-                return 0;
-            }
-
-            bool calculateAverage = true;
-            if ((_items.Count == _context.ItemCount) && _items[_items.Count - 1].Position.HasValue)
-            {
-                calculateAverage = false;
-            }
-
-            UvMeasure? lastPosition = null;
-            double maxV = 0;
-
-            int itemCount = _items.Count;
-            for (int i = _items.Count - 1; i >= 0; i--)
-            {
-                var item = _items[i];
-                if (item.Position == null)
-                {
-                    itemCount--;
-                    continue;
-                }
-
-                if (lastPosition != null)
-                {
-                    if (lastPosition.Value.V > item.Position.Value.V)
-                    {
-                        // This is a row above the last item. Exit and calculate the average
-                        break;
-                    }
-                }
-
-                lastPosition = item.Position;
-                maxV = Math.Max(maxV, item.Measure!.Value.V);
-            }
-
-            double totalHeight = lastPosition!.Value.V + maxV;
-            if (calculateAverage)
-            {
-                return (totalHeight / itemCount) * _context.ItemCount;
-            }
-            else
-            {
-                return totalHeight;
-            }
-        }
-
-        internal void RecycleElementAt(int index)
-        {
-            var element = _context.GetOrCreateElementAt(index);
-            _context.RecycleElement(element);
-        }
-    }
-}

+ 0 - 4
src/Avalonia.Controls.ItemsRepeater/Properties/AssemblyInfo.cs

@@ -1,4 +0,0 @@
-using Avalonia.Metadata;
-
-[assembly: XmlnsDefinition("https://github.com/avaloniaui", "Avalonia.Controls")]
-[assembly: XmlnsDefinition("https://github.com/avaloniaui", "Avalonia.Layout")]

+ 0 - 22
tests/Avalonia.Controls.ItemsRepeater.UnitTests/Avalonia.Controls.ItemsRepeater.UnitTests.csproj

@@ -1,22 +0,0 @@
-<Project Sdk="Microsoft.NET.Sdk" ToolsVersion="15.0">
-  <PropertyGroup>
-    <TargetFramework>$(AvsCurrentTargetFramework)</TargetFramework>
-    <OutputType>Library</OutputType>
-    <IsTestProject>true</IsTestProject>
-  </PropertyGroup>
-  <Import Project="..\..\build\UnitTests.NetCore.targets" />
-  <Import Project="..\..\build\UnitTests.NetFX.props" />
-  <Import Project="..\..\build\Moq.props" />
-  <Import Project="..\..\build\XUnit.props" />
-  <Import Project="..\..\build\Rx.props" />
-  <Import Project="..\..\build\Microsoft.Reactive.Testing.props" />
-  <Import Project="..\..\build\Base.props" />
-  <Import Project="..\..\build\SharedVersion.props" />
-  <ItemGroup>
-    <ProjectReference Include="..\..\src\Avalonia.Controls.ItemsRepeater\Avalonia.Controls.ItemsRepeater.csproj" />
-    <ProjectReference Include="..\Avalonia.UnitTests\Avalonia.UnitTests.csproj" />
-  </ItemGroup>
-  <ItemGroup>
-    <Service Include="{82a7f48d-3b50-4b1e-b82e-3ada8210c358}" />
-  </ItemGroup>
-</Project>

+ 0 - 24
tests/Avalonia.Controls.ItemsRepeater.UnitTests/ItemsRepeaterTests.cs

@@ -1,24 +0,0 @@
-using System.Collections.ObjectModel;
-using Xunit;
-
-namespace Avalonia.Controls.UnitTests
-{
-    public class ItemsRepeaterTests
-    {
-        [Fact]
-        public void Can_Reassign_Items()
-        {
-            var target = new ItemsRepeater();
-            target.ItemsSource = new ObservableCollection<string>();
-            target.ItemsSource = new ObservableCollection<string>();
-        }
-
-        [Fact]
-        public void Can_Reassign_Items_To_Null()
-        {
-            var target = new ItemsRepeater();
-            target.ItemsSource = new ObservableCollection<string>();
-            target.ItemsSource = null;
-        }
-    }
-}

+ 0 - 336
tests/Avalonia.Controls.ItemsRepeater.UnitTests/NonVirtualizingStackLayoutTests.cs

@@ -1,336 +0,0 @@
-using System.Collections.Generic;
-using System.Linq;
-using Avalonia.Controls;
-using Avalonia.Layout;
-using Xunit;
-
-namespace Avalonia.Base.UnitTests.Layout
-{
-    public class NonVirtualizingStackLayoutTests
-    {
-        [Fact]
-        public void Lays_Out_Children_Vertically()
-        {
-            var target = new NonVirtualizingStackLayout { Orientation = Orientation.Vertical };
-            var context = CreateContext(new[]
-            {
-                new Border { Height = 20, Width = 120 },
-                new Border { Height = 30 },
-                new Border { Height = 50 },
-            });
-
-            var desiredSize = target.Measure(context, Size.Infinity);
-            var arrangeSize = target.Arrange(context, desiredSize);
-
-            Assert.Equal(new Size(120, 100), desiredSize);
-            Assert.Equal(new Size(120, 100), arrangeSize);
-            Assert.Equal(new Rect(0, 0, 120, 20), context.Children[0].Bounds);
-            Assert.Equal(new Rect(0, 20, 120, 30), context.Children[1].Bounds);
-            Assert.Equal(new Rect(0, 50, 120, 50), context.Children[2].Bounds);
-        }
-
-        [Fact]
-        public void Lays_Out_Children_Horizontally()
-        {
-            var target = new NonVirtualizingStackLayout { Orientation = Orientation.Horizontal };
-            var context = CreateContext(new[]
-            {
-                new Border { Width = 20, Height = 120 },
-                new Border { Width = 30 },
-                new Border { Width = 50 },
-            });
-
-            var desiredSize = target.Measure(context, Size.Infinity);
-            var arrangeSize = target.Arrange(context, desiredSize);
-
-            Assert.Equal(new Size(100, 120), desiredSize);
-            Assert.Equal(new Size(100, 120), arrangeSize);
-            Assert.Equal(new Rect(0, 0, 20, 120), context.Children[0].Bounds);
-            Assert.Equal(new Rect(20, 0, 30, 120), context.Children[1].Bounds);
-            Assert.Equal(new Rect(50, 0, 50, 120), context.Children[2].Bounds);
-        }
-
-        [Fact]
-        public void Lays_Out_Children_Vertically_With_Spacing()
-        {
-            var target = new NonVirtualizingStackLayout 
-            { 
-                Orientation = Orientation.Vertical,
-                Spacing = 10,
-            };
-
-            var context = CreateContext(new[]
-            {
-                new Border { Height = 20, Width = 120 },
-                new Border { Height = 30 },
-                new Border { Height = 50 },
-            });
-
-            var desiredSize = target.Measure(context, Size.Infinity);
-            var arrangeSize = target.Arrange(context, desiredSize);
-
-            Assert.Equal(new Size(120, 120), desiredSize);
-            Assert.Equal(new Size(120, 120), arrangeSize);
-            Assert.Equal(new Rect(0, 0, 120, 20), context.Children[0].Bounds);
-            Assert.Equal(new Rect(0, 30, 120, 30), context.Children[1].Bounds);
-            Assert.Equal(new Rect(0, 70, 120, 50), context.Children[2].Bounds);
-        }
-
-        [Fact]
-        public void Lays_Out_Children_Horizontally_With_Spacing()
-        {
-            var target = new NonVirtualizingStackLayout 
-            { 
-                Orientation = Orientation.Horizontal,
-                Spacing = 10,
-            };
-
-            var context = CreateContext(new[]
-            {
-                new Border { Width = 20, Height = 120 },
-                new Border { Width = 30 },
-                new Border { Width = 50 },
-            });
-
-            var desiredSize = target.Measure(context, Size.Infinity);
-            var arrangeSize = target.Arrange(context, desiredSize);
-
-            Assert.Equal(new Size(120, 120), desiredSize);
-            Assert.Equal(new Size(120, 120), arrangeSize);
-            Assert.Equal(new Rect(0, 0, 20, 120), context.Children[0].Bounds);
-            Assert.Equal(new Rect(30, 0, 30, 120), context.Children[1].Bounds);
-            Assert.Equal(new Rect(70, 0, 50, 120), context.Children[2].Bounds);
-        }
-
-        [Fact]
-        public void Arranges_Vertical_Children_With_Correct_Bounds()
-        {
-            var target = new NonVirtualizingStackLayout
-            {
-                Orientation = Orientation.Vertical
-            };
-            
-            var context = CreateContext(new[]
-            {
-                new TestControl
-                {
-                    HorizontalAlignment = HorizontalAlignment.Left,
-                    MeasureSize = new Size(50, 10),
-                },
-                new TestControl
-                {
-                    HorizontalAlignment = HorizontalAlignment.Left,
-                    MeasureSize = new Size(150, 10),
-                },
-                new TestControl
-                {
-                    HorizontalAlignment = HorizontalAlignment.Center,
-                    MeasureSize = new Size(50, 10),
-                },
-                new TestControl
-                {
-                    HorizontalAlignment = HorizontalAlignment.Center,
-                    MeasureSize = new Size(150, 10),
-                },
-                new TestControl
-                {
-                    HorizontalAlignment = HorizontalAlignment.Right,
-                    MeasureSize = new Size(50, 10),
-                },
-                new TestControl
-                {
-                    HorizontalAlignment = HorizontalAlignment.Right,
-                    MeasureSize = new Size(150, 10),
-                },
-                new TestControl
-                {
-                    HorizontalAlignment = HorizontalAlignment.Stretch,
-                    MeasureSize = new Size(50, 10),
-                },
-                new TestControl
-                {
-                    HorizontalAlignment = HorizontalAlignment.Stretch,
-                    MeasureSize = new Size(150, 10),
-                },
-            });
-
-            var desiredSize = target.Measure(context, new Size(100, 150));
-            Assert.Equal(new Size(100, 80), desiredSize);
-
-            target.Arrange(context, desiredSize);
-
-            var bounds = context.Children.Select(x => x.Bounds).ToArray();
-
-            Assert.Equal(
-                new[]
-                {
-                    new Rect(0, 0, 50, 10),
-                    new Rect(0, 10, 100, 10),
-                    new Rect(25, 20, 50, 10),
-                    new Rect(0, 30, 100, 10),
-                    new Rect(50, 40, 50, 10),
-                    new Rect(0, 50, 100, 10),
-                    new Rect(0, 60, 100, 10),
-                    new Rect(0, 70, 100, 10),
-
-                }, bounds);
-        }
-
-        [Fact]
-        public void Arranges_Horizontal_Children_With_Correct_Bounds()
-        {
-            var target = new NonVirtualizingStackLayout
-            {
-                Orientation = Orientation.Horizontal
-            };
-
-            var context = CreateContext(new[]
-            {
-                new TestControl
-                {
-                    VerticalAlignment = VerticalAlignment.Top,
-                    MeasureSize = new Size(10, 50),
-                },
-                new TestControl
-                {
-                    VerticalAlignment = VerticalAlignment.Top,
-                    MeasureSize = new Size(10, 150),
-                },
-                new TestControl
-                {
-                    VerticalAlignment = VerticalAlignment.Center,
-                    MeasureSize = new Size(10, 50),
-                },
-                new TestControl
-                {
-                    VerticalAlignment = VerticalAlignment.Center,
-                    MeasureSize = new Size(10, 150),
-                },
-                new TestControl
-                {
-                    VerticalAlignment = VerticalAlignment.Bottom,
-                    MeasureSize = new Size(10, 50),
-                },
-                new TestControl
-                {
-                    VerticalAlignment = VerticalAlignment.Bottom,
-                    MeasureSize = new Size(10, 150),
-                },
-                new TestControl
-                {
-                    VerticalAlignment = VerticalAlignment.Stretch,
-                    MeasureSize = new Size(10, 50),
-                },
-                new TestControl
-                {
-                    VerticalAlignment = VerticalAlignment.Stretch,
-                    MeasureSize = new Size(10, 150),
-                },
-            });
-
-            var desiredSize = target.Measure(context, new Size(150, 100));
-            Assert.Equal(new Size(80, 100), desiredSize);
-
-            target.Arrange(context, desiredSize);
-
-            var bounds = context.Children.Select(x => x.Bounds).ToArray();
-
-            Assert.Equal(
-                new[]
-                {
-                    new Rect(0, 0, 10, 50),
-                    new Rect(10, 0, 10, 100),
-                    new Rect(20, 25, 10, 50),
-                    new Rect(30, 0, 10, 100),
-                    new Rect(40, 50, 10, 50),
-                    new Rect(50, 0, 10, 100),
-                    new Rect(60, 0, 10, 100),
-                    new Rect(70, 0, 10, 100),
-                }, bounds);
-        }
-
-        [Theory]
-        [InlineData(Orientation.Horizontal)]
-        [InlineData(Orientation.Vertical)]
-        public void Spacing_Not_Added_For_Invisible_Children(Orientation orientation)
-        {
-            var targetThreeChildrenOneInvisble = new NonVirtualizingStackLayout
-            {
-                Orientation = orientation,
-                Spacing = 40,
-            };
-
-            var contextThreeChildrenOneInvisble = CreateContext(new[]
-            {
-                new StackPanel { Width = 10, Height= 10, IsVisible = false },
-                new StackPanel { Width = 10, Height= 10 },
-                new StackPanel { Width = 10, Height= 10 },
-            });
-
-            var targetTwoChildrenNoneInvisible = new NonVirtualizingStackLayout
-            {
-                Spacing = 40,
-                Orientation = orientation,
-            };
-
-            var contextTwoChildrenNoneInvisible = CreateContext(new[]
-            {
-                new StackPanel { Width = 10, Height = 10 },
-                new StackPanel { Width = 10, Height = 10 }
-            });
-
-            var desiredSize1 = targetThreeChildrenOneInvisble.Measure(contextThreeChildrenOneInvisble, Size.Infinity);
-            var desiredSize2 = targetTwoChildrenNoneInvisible.Measure(contextTwoChildrenNoneInvisible, Size.Infinity);
- 
-            Assert.Equal(desiredSize2, desiredSize1);
-        }
-
-        [Theory]
-        [InlineData(Orientation.Horizontal)]
-        [InlineData(Orientation.Vertical)]
-        public void Only_Arrange_Visible_Children(Orientation orientation)
-        {
-            var hiddenPanel = new Panel { Width = 10, Height = 10, IsVisible = false };
-            var panel = new Panel { Width = 10, Height = 10 };
-
-            var target = new NonVirtualizingStackLayout
-            {
-                Spacing = 40,
-                Orientation = orientation,
-            };
-
-            var context = CreateContext(new[]
-            {
-                hiddenPanel,
-                panel
-            });
-
-            var desiredSize = target.Measure(context, Size.Infinity);
-            var arrangeSize = target.Arrange(context, desiredSize);
-            Assert.Equal(new Size(10, 10), arrangeSize);
-        }
-
-        private static NonVirtualizingLayoutContext CreateContext(Control[] children)
-        {
-            return new TestLayoutContext(children);
-        }
-
-        private class TestLayoutContext : NonVirtualizingLayoutContext
-        {
-            public TestLayoutContext(Control[] children) => ChildrenCore = children;
-            protected override IReadOnlyList<Layoutable> ChildrenCore { get; }
-        }
-
-        private class TestControl : Control
-        {
-            public Size MeasureConstraint { get; private set; }
-            public Size MeasureSize { get; set; }
-
-            protected override Size MeasureOverride(Size availableSize)
-            {
-                MeasureConstraint = availableSize;
-                return MeasureSize;
-            }
-        }
-    }
-}

+ 0 - 1
tests/Avalonia.LeakTests/Avalonia.LeakTests.csproj

@@ -11,7 +11,6 @@
   <ItemGroup>
     <ProjectReference Include="..\..\src\Avalonia.Controls.ColorPicker\Avalonia.Controls.ColorPicker.csproj" />
     <ProjectReference Include="..\..\src\Avalonia.Controls.DataGrid\Avalonia.Controls.DataGrid.csproj" />
-    <ProjectReference Include="..\..\src\Avalonia.Controls.ItemsRepeater\Avalonia.Controls.ItemsRepeater.csproj" />
     <ProjectReference Include="..\..\src\Markup\Avalonia.Markup.Xaml\Avalonia.Markup.Xaml.csproj" />
     <ProjectReference Include="..\..\src\Markup\Avalonia.Markup\Avalonia.Markup.csproj" />
     <ProjectReference Include="..\..\src\Avalonia.Base\Avalonia.Base.csproj" />

+ 1 - 35
tests/Avalonia.LeakTests/ControlTests.cs

@@ -714,40 +714,6 @@ namespace Avalonia.LeakTests
             }
         }
 
-        [Fact]
-        public void ItemsRepeater_Is_Freed()
-        {
-            using (Start())
-            {
-                Func<Window> run = () =>
-                {
-                    var window = new Window
-                    {
-                        Content = new ItemsRepeater(),
-                    };
-
-                    window.Show();
-
-                    window.LayoutManager.ExecuteInitialLayoutPass();
-                    Assert.IsType<ItemsRepeater>(window.Presenter.Child);
-
-                    window.Content = null;
-                    window.LayoutManager.ExecuteLayoutPass();
-                    Assert.Null(window.Presenter.Child);
-
-                    return window;
-                };
-
-                var result = run();
-
-                // Process all Loaded events to free control reference(s)
-                Dispatcher.UIThread.RunJobs(DispatcherPriority.Loaded);
-
-                dotMemory.Check(memory =>
-                    Assert.Equal(0, memory.GetObjects(where => where.Type.Is<ItemsRepeater>()).ObjectsCount));
-            }
-        }
-
         [Fact]
         public void ElementName_Binding_In_DataTemplate_Is_Freed()
         {
@@ -836,7 +802,7 @@ namespace Avalonia.LeakTests
                     var gesture1 = new KeyGesture(Key.A, KeyModifiers.Control);
                     var tl = new Window
                     {
-                        Content = new ItemsRepeater(),
+                        Content = new ItemsControl(),
                     };
 
                     tl.Show();

+ 0 - 1
tests/Avalonia.Markup.Xaml.UnitTests/Avalonia.Markup.Xaml.UnitTests.csproj

@@ -17,7 +17,6 @@
     <ProjectReference Include="..\..\src\Markup\Avalonia.Markup\Avalonia.Markup.csproj" />
     <ProjectReference Include="..\..\src\Avalonia.Base\Avalonia.Base.csproj" />
     <ProjectReference Include="..\..\src\Avalonia.Controls\Avalonia.Controls.csproj" />
-    <ProjectReference Include="..\..\src\Avalonia.Controls.ItemsRepeater\Avalonia.Controls.ItemsRepeater.csproj" />
     <ProjectReference Include="..\..\src\Avalonia.Themes.Simple\Avalonia.Themes.Simple.csproj" />
     <ProjectReference Include="..\Avalonia.UnitTests\Avalonia.UnitTests.csproj" />
   </ItemGroup>

+ 0 - 52
tests/Avalonia.Markup.Xaml.UnitTests/Xaml/StyleTests.cs

@@ -16,11 +16,6 @@ namespace Avalonia.Markup.Xaml.UnitTests.Xaml
 {
     public class StyleTests : XamlTestBase
     {
-        static StyleTests()
-        {
-            GC.KeepAlive(typeof(ItemsRepeater));
-        }
-
         [Fact]
         public void Color_Can_Be_Added_To_Style_Resources()
         {
@@ -399,53 +394,6 @@ namespace Avalonia.Markup.Xaml.UnitTests.Xaml
             }
         }
 
-        [Fact]
-        public void Style_Can_Use_NthChild_Selector_With_ItemsRepeater()
-        {
-            GC.KeepAlive(typeof(ItemsRepeater));
-            
-            using (UnitTestApplication.Start(TestServices.StyledWindow))
-            {
-                var xaml = @"
-<Window xmlns='https://github.com/avaloniaui'
-             xmlns:x='http://schemas.microsoft.com/winfx/2006/xaml'>
-    <Window.Styles>
-        <Style Selector='TextBlock'>
-            <Setter Property='Foreground' Value='Transparent'/>
-        </Style>
-        <Style Selector='TextBlock:nth-child(2n)'>
-            <Setter Property='Foreground' Value='{Binding}'/>
-        </Style>
-    </Window.Styles>
-    <ItemsRepeater x:Name='list' />
-</Window>";
-                var window = (Window)AvaloniaRuntimeXamlLoader.Load(xaml);
-                var collection = new ObservableCollection<IBrush>()
-                {
-                    Brushes.Red, Brushes.Green, Brushes.Blue
-                };
-
-                var list = window.FindControl<ItemsRepeater>("list");
-                list.ItemsSource = collection;
-
-                window.Show();
-
-                IEnumerable<IBrush> GetColors() => Enumerable.Range(0, list.ItemsSourceView.Count)
-                    .Select(t => (list.GetOrCreateElement(t) as TextBlock)!.Foreground);
-
-                Assert.Equal(new[] { Brushes.Transparent, Brushes.Green, Brushes.Transparent }, GetColors());
-
-                collection.Remove(Brushes.Green);
-
-                Assert.Equal(new[] { Brushes.Transparent, Brushes.Blue }, GetColors().ToList());
-
-                collection.Add(Brushes.Violet);
-                collection.Add(Brushes.Black);
-
-                Assert.Equal(new[] { Brushes.Transparent, Brushes.Blue, Brushes.Transparent, Brushes.Black }, GetColors());
-            }
-        }
-
         [Fact]
         public void Style_Can_Use_Or_Selector_1()
         {

+ 0 - 1
tests/Avalonia.Markup.Xaml.UnitTests/XamlTestBase.cs

@@ -16,7 +16,6 @@ namespace Avalonia.Markup.Xaml.UnitTests
         public static void Init()
         {
             var _ = typeof(Binding);
-            GC.KeepAlive(typeof(ItemsRepeater).Assembly);
         }
     }