Browse Source

Moved attached layout code to Avalonia.Layout.

Also involved moving `Orientation` enum.
Steven Kirk 6 years ago
parent
commit
2210b441a2
41 changed files with 166 additions and 126 deletions
  1. 1 0
      samples/ControlCatalog/Pages/ItemsRepeaterPage.xaml.cs
  2. 1 0
      samples/VirtualizationDemo/ViewModels/MainWindowViewModel.cs
  3. 1 0
      src/Avalonia.Controls.DataGrid/DataGrid.cs
  4. 1 0
      src/Avalonia.Controls/ContextMenu.cs
  5. 1 0
      src/Avalonia.Controls/GridSplitter.cs
  6. 1 0
      src/Avalonia.Controls/Menu.cs
  7. 1 0
      src/Avalonia.Controls/Presenters/ItemVirtualizer.cs
  8. 1 0
      src/Avalonia.Controls/Primitives/ScrollBar.cs
  9. 1 0
      src/Avalonia.Controls/Primitives/TabStrip.cs
  10. 1 0
      src/Avalonia.Controls/Primitives/Track.cs
  11. 4 3
      src/Avalonia.Controls/ProgressBar.cs
  12. 11 12
      src/Avalonia.Controls/Repeaters/ItemsRepeater.cs
  13. 3 2
      src/Avalonia.Controls/Repeaters/RepeaterLayoutContext.cs
  14. 1 0
      src/Avalonia.Controls/Repeaters/ViewManager.cs
  15. 1 1
      src/Avalonia.Controls/Repeaters/ViewportManager.cs
  16. 1 0
      src/Avalonia.Controls/Slider.cs
  17. 2 2
      src/Avalonia.Controls/StackPanel.cs
  18. 1 0
      src/Avalonia.Controls/WrapPanel.cs
  19. 6 6
      src/Avalonia.Layout/AttachedLayout.cs
  20. 22 23
      src/Avalonia.Layout/ElementManager.cs
  21. 7 7
      src/Avalonia.Layout/FlowLayoutAlgorithm.cs
  22. 4 8
      src/Avalonia.Layout/IFlowLayoutAlgorithmDelegates.cs
  23. 1 1
      src/Avalonia.Layout/LayoutContext.cs
  24. 2 2
      src/Avalonia.Layout/NonVirtualizingLayout.cs
  25. 1 1
      src/Avalonia.Layout/Orientation.cs
  26. 23 17
      src/Avalonia.Layout/OrientationBasedMeasures.cs
  27. 10 10
      src/Avalonia.Layout/StackLayout.cs
  28. 1 1
      src/Avalonia.Layout/StackLayoutState.cs
  29. 10 10
      src/Avalonia.Layout/UniformGridLayout.cs
  30. 3 5
      src/Avalonia.Layout/UniformGridLayoutState.cs
  31. 1 1
      src/Avalonia.Layout/Utils/ListUtils.cs
  32. 23 4
      src/Avalonia.Layout/VirtualizingLayout.cs
  33. 7 7
      src/Avalonia.Layout/VirtualizingLayoutContext.cs
  34. 1 0
      tests/Avalonia.Base.UnitTests/Data/DefaultValueConverterTests.cs
  35. 2 1
      tests/Avalonia.Controls.UnitTests/Primitives/RangeBaseTests.cs
  36. 1 0
      tests/Avalonia.Controls.UnitTests/Primitives/TrackTests.cs
  37. 2 1
      tests/Avalonia.Controls.UnitTests/ScrollViewerTests.cs
  38. 1 0
      tests/Avalonia.Controls.UnitTests/SliderTests.cs
  39. 2 1
      tests/Avalonia.Controls.UnitTests/WrapPanelTests.cs
  40. 1 0
      tests/Avalonia.Markup.Xaml.UnitTests/Converters/NullableConverterTests.cs
  41. 1 0
      tests/Avalonia.Visuals.UnitTests/VisualTree/VisualExtensions_GetVisualsAt.cs

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

@@ -5,6 +5,7 @@ using System.Linq;
 using Avalonia.Controls;
 using Avalonia.Controls.Primitives;
 using Avalonia.Controls.Repeaters;
+using Avalonia.Layout;
 using Avalonia.Markup.Xaml;
 
 namespace ControlCatalog.Pages

+ 1 - 0
samples/VirtualizationDemo/ViewModels/MainWindowViewModel.cs

@@ -9,6 +9,7 @@ using Avalonia.Controls;
 using Avalonia.Controls.Primitives;
 using ReactiveUI.Legacy;
 using ReactiveUI;
+using Avalonia.Layout;
 
 namespace VirtualizationDemo.ViewModels
 {

+ 1 - 0
src/Avalonia.Controls.DataGrid/DataGrid.cs

@@ -24,6 +24,7 @@ using System.Linq;
 using Avalonia.Input.Platform;
 using System.ComponentModel.DataAnnotations;
 using Avalonia.Controls.Utils;
+using Avalonia.Layout;
 
 namespace Avalonia.Controls
 {

+ 1 - 0
src/Avalonia.Controls/ContextMenu.cs

@@ -7,6 +7,7 @@ using Avalonia.Controls.Platform;
 using Avalonia.Controls.Primitives;
 using Avalonia.Controls.Templates;
 using Avalonia.Input;
+using Avalonia.Layout;
 using Avalonia.LogicalTree;
 
 namespace Avalonia.Controls

+ 1 - 0
src/Avalonia.Controls/GridSplitter.cs

@@ -6,6 +6,7 @@ using System.Collections.Generic;
 using System.Linq;
 using Avalonia.Controls.Primitives;
 using Avalonia.Input;
+using Avalonia.Layout;
 using Avalonia.VisualTree;
 
 namespace Avalonia.Controls

+ 1 - 0
src/Avalonia.Controls/Menu.cs

@@ -5,6 +5,7 @@ using Avalonia.Controls.Platform;
 using Avalonia.Controls.Templates;
 using Avalonia.Input;
 using Avalonia.Interactivity;
+using Avalonia.Layout;
 
 namespace Avalonia.Controls
 {

+ 1 - 0
src/Avalonia.Controls/Presenters/ItemVirtualizer.cs

@@ -8,6 +8,7 @@ using System.Reactive.Linq;
 using Avalonia.Controls.Primitives;
 using Avalonia.Controls.Utils;
 using Avalonia.Input;
+using Avalonia.Layout;
 
 namespace Avalonia.Controls.Presenters
 {

+ 1 - 0
src/Avalonia.Controls/Primitives/ScrollBar.cs

@@ -7,6 +7,7 @@ using System.Reactive.Linq;
 using Avalonia.Data;
 using Avalonia.Interactivity;
 using Avalonia.Input;
+using Avalonia.Layout;
 
 namespace Avalonia.Controls.Primitives
 {

+ 1 - 0
src/Avalonia.Controls/Primitives/TabStrip.cs

@@ -4,6 +4,7 @@
 using Avalonia.Controls.Generators;
 using Avalonia.Controls.Templates;
 using Avalonia.Input;
+using Avalonia.Layout;
 
 namespace Avalonia.Controls.Primitives
 {

+ 1 - 0
src/Avalonia.Controls/Primitives/Track.cs

@@ -3,6 +3,7 @@
 
 using System;
 using Avalonia.Input;
+using Avalonia.Layout;
 using Avalonia.Metadata;
 
 namespace Avalonia.Controls.Primitives

+ 4 - 3
src/Avalonia.Controls/ProgressBar.cs

@@ -3,6 +3,7 @@
 
 
 using Avalonia.Controls.Primitives;
+using Avalonia.Layout;
 
 namespace Avalonia.Controls
 {
@@ -33,8 +34,8 @@ namespace Avalonia.Controls
 
         static ProgressBar()
         {
-            PseudoClass<ProgressBar, Orientation>(OrientationProperty, o => o == Avalonia.Controls.Orientation.Vertical, ":vertical");
-            PseudoClass<ProgressBar, Orientation>(OrientationProperty, o => o == Avalonia.Controls.Orientation.Horizontal, ":horizontal");
+            PseudoClass<ProgressBar, Orientation>(OrientationProperty, o => o == Orientation.Vertical, ":vertical");
+            PseudoClass<ProgressBar, Orientation>(OrientationProperty, o => o == Orientation.Horizontal, ":horizontal");
             PseudoClass<ProgressBar>(IsIndeterminateProperty, ":indeterminate");
 
             ValueProperty.Changed.AddClassHandler<ProgressBar>(x => x.UpdateIndicatorWhenPropChanged);
@@ -120,4 +121,4 @@ namespace Avalonia.Controls
             UpdateIndicator(Bounds.Size);
         }
     }
-}
+}

+ 11 - 12
src/Avalonia.Controls/Repeaters/ItemsRepeater.cs

@@ -8,6 +8,7 @@ using System.Collections;
 using System.Collections.Specialized;
 using Avalonia.Controls.Templates;
 using Avalonia.Input;
+using Avalonia.Layout;
 
 namespace Avalonia.Controls.Repeaters
 {
@@ -38,8 +39,8 @@ namespace Avalonia.Controls.Repeaters
         /// <summary>
         /// Defines the <see cref="Layout"/> property.
         /// </summary>
-        public static readonly AvaloniaProperty<Layout> LayoutProperty =
-            AvaloniaProperty.Register<ItemsRepeater, Layout>(nameof(Layout), new StackLayout());
+        public static readonly AvaloniaProperty<AttachedLayout> LayoutProperty =
+            AvaloniaProperty.Register<ItemsRepeater, AttachedLayout>(nameof(Layout), new StackLayout());
 
         /// <summary>
         /// Defines the <see cref="VerticalCacheLength"/> property.
@@ -58,7 +59,6 @@ namespace Avalonia.Controls.Repeaters
         private IEnumerable _items;
         private VirtualizingLayoutContext _layoutContext;
         private NotifyCollectionChangedEventArgs _processingItemsSourceChange;
-        private Size _lastAvailableSize;
         private bool _isLayoutInProgress;
         private ItemsRepeaterElementPreparedEventArgs _elementPreparedArgs;
         private ItemsRepeaterElementClearingEventArgs _elementClearingArgs;
@@ -87,7 +87,7 @@ namespace Avalonia.Controls.Repeaters
         /// The layout used to size and position elements. The default is a StackLayout with
         /// vertical orientation.
         /// </value>
-        public Layout Layout
+        public AttachedLayout Layout
         {
             get => GetValue(LayoutProperty);
             set => SetValue(LayoutProperty, value);
@@ -300,7 +300,6 @@ namespace Avalonia.Controls.Repeaters
                 }
 
                 _viewportManager.SetLayoutExtent(extent);
-                _lastAvailableSize = availableSize;
                 return desiredSize;
             }
             finally
@@ -396,7 +395,7 @@ namespace Avalonia.Controls.Repeaters
             }
             else if (property == LayoutProperty)
             {
-                OnLayoutChanged((Layout)args.OldValue, (Layout)args.NewValue);
+                OnLayoutChanged((AttachedLayout)args.OldValue, (AttachedLayout)args.NewValue);
             }
             else if (property == HorizontalCacheLengthProperty)
             {
@@ -479,7 +478,7 @@ namespace Avalonia.Controls.Repeaters
                     throw new InvalidOperationException("Cannot make an Anchor when there is no attached layout.");
                 }
 
-                element = GetLayoutContext().GetOrCreateElementAt(index);
+                element = (IControl)GetLayoutContext().GetOrCreateElementAt(index);
                 element.Measure(Size.Infinity);
             }
 
@@ -566,7 +565,7 @@ namespace Avalonia.Controls.Repeaters
                 if (Layout is VirtualizingLayout virtualLayout)
                 {
                     var args = new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Reset);
-                    virtualLayout.OnItemsChangedCore(GetLayoutContext(), newValue, args);
+                    virtualLayout.OnItemsChanged(GetLayoutContext(), newValue, args);
                 }
                 else if (Layout is NonVirtualizingLayout nonVirtualLayout)
                 {
@@ -605,14 +604,14 @@ namespace Avalonia.Controls.Repeaters
 
                     try
                     {
-                        virtualLayout.OnItemsChangedCore(GetLayoutContext(), newValue, args);
+                        virtualLayout.OnItemsChanged(GetLayoutContext(), newValue, args);
                     }
                     finally
                     {
                         _processingItemsSourceChange = null;
                     }
                 }
-                else if (Layout is NonVirtualizingLayout nonVirtualLayout)
+                else if (Layout is NonVirtualizingLayout)
                 {
                     // Walk through all the elements and make sure they are cleared for
                     // non-virtualizing layouts.
@@ -631,7 +630,7 @@ namespace Avalonia.Controls.Repeaters
             InvalidateMeasure();
         }
 
-        private void OnLayoutChanged(Layout oldValue, Layout newValue)
+        private void OnLayoutChanged(AttachedLayout oldValue, AttachedLayout newValue)
         {
             if (_isLayoutInProgress)
             {
@@ -693,7 +692,7 @@ namespace Avalonia.Controls.Repeaters
                 {
                     if (Layout is VirtualizingLayout virtualLayout)
                     {
-                        virtualLayout.OnItemsChangedCore(GetLayoutContext(), sender, args);
+                        virtualLayout.OnItemsChanged(GetLayoutContext(), sender, args);
                     }
                     else
                     {

+ 3 - 2
src/Avalonia.Controls/Repeaters/RepeaterLayoutContext.cs

@@ -6,6 +6,7 @@
 using System;
 using System.Collections.Generic;
 using System.Text;
+using Avalonia.Layout;
 
 namespace Avalonia.Controls.Repeaters
 {
@@ -47,7 +48,7 @@ namespace Avalonia.Controls.Repeaters
 
         protected override int ItemCountCore() => _owner.ItemsSourceView?.Count ?? 0;
 
-        protected override IControl GetOrCreateElementAtCore(int index, ElementRealizationOptions options)
+        protected override ILayoutable GetOrCreateElementAtCore(int index, ElementRealizationOptions options)
         {
             return _owner.GetElementImpl(
                 index,
@@ -57,7 +58,7 @@ namespace Avalonia.Controls.Repeaters
 
         protected override object GetItemAtCore(int index) => _owner.ItemsSourceView.GetAt(index);
 
-        protected override void RecycleElementCore(IControl element) => _owner.ClearElementImpl(element);
+        protected override void RecycleElementCore(ILayoutable element) => _owner.ClearElementImpl((IControl)element);
 
         protected override Rect RealizationRectCore() => _owner.RealizationWindow;
     }

+ 1 - 0
src/Avalonia.Controls/Repeaters/ViewManager.cs

@@ -9,6 +9,7 @@ using System.Collections.Specialized;
 using Avalonia.Controls.Templates;
 using Avalonia.Input;
 using Avalonia.Interactivity;
+using Avalonia.Layout;
 using Avalonia.VisualTree;
 
 namespace Avalonia.Controls.Repeaters

+ 1 - 1
src/Avalonia.Controls/Repeaters/ViewportManager.cs

@@ -234,7 +234,7 @@ namespace Avalonia.Controls.Repeaters
             ////element.CanBeScrollAnchor(true);
         }
 
-        public void OnElementCleared(IControl element)
+        public void OnElementCleared(ILayoutable element)
         {
             ////element.CanBeScrollAnchor(false);
         }

+ 1 - 0
src/Avalonia.Controls/Slider.cs

@@ -5,6 +5,7 @@ using System;
 using Avalonia.Controls.Primitives;
 using Avalonia.Input;
 using Avalonia.Interactivity;
+using Avalonia.Layout;
 
 namespace Avalonia.Controls
 {

+ 2 - 2
src/Avalonia.Controls/StackPanel.cs

@@ -17,13 +17,13 @@ namespace Avalonia.Controls
         /// Defines the <see cref="Spacing"/> property.
         /// </summary>
         public static readonly StyledProperty<double> SpacingProperty =
-            AvaloniaProperty.Register<StackPanel, double>(nameof(Spacing));
+            StackLayout.SpacingProperty.AddOwner<StackPanel>();
 
         /// <summary>
         /// Defines the <see cref="Orientation"/> property.
         /// </summary>
         public static readonly StyledProperty<Orientation> OrientationProperty =
-            AvaloniaProperty.Register<StackPanel, Orientation>(nameof(Orientation), Orientation.Vertical);
+            StackLayout.OrientationProperty.AddOwner<StackPanel>();
 
         /// <summary>
         /// Initializes static members of the <see cref="StackPanel"/> class.

+ 1 - 0
src/Avalonia.Controls/WrapPanel.cs

@@ -6,6 +6,7 @@ using System.Diagnostics;
 using System.Linq;
 
 using Avalonia.Input;
+using Avalonia.Layout;
 using Avalonia.Utilities;
 
 using static System.Math;

+ 6 - 6
src/Avalonia.Controls/Repeaters/Layout.cs → src/Avalonia.Layout/AttachedLayout.cs

@@ -5,12 +5,12 @@
 
 using System;
 
-namespace Avalonia.Controls.Repeaters
+namespace Avalonia.Layout
 {
     /// <summary>
     /// Represents the base class for an object that sizes and arranges child elements for a host.
     /// </summary>
-    public abstract class Layout : AvaloniaObject
+    public abstract class AttachedLayout : AvaloniaObject
     {
         internal string LayoutId { get; set; }
 
@@ -26,7 +26,7 @@ namespace Avalonia.Controls.Repeaters
 
         /// <summary>
         /// Initializes any per-container state the layout requires when it is attached to an
-        /// <see cref="IControl"/> container.
+        /// <see cref="ILayoutable"/> container.
         /// </summary>
         /// <param name="context">
         /// The context object that facilitates communication between the layout and its host
@@ -49,7 +49,7 @@ namespace Avalonia.Controls.Repeaters
         public abstract void InitializeForContext(LayoutContext context);
 
         /// <summary>
-        /// Removes any state the layout previously stored on the IControl container.
+        /// 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
@@ -61,7 +61,7 @@ namespace Avalonia.Controls.Repeaters
         /// 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 IControl children.
+        /// for each of the container’s ILayoutable children.
         /// </summary>
         /// <param name="context">
         /// The context object that facilitates communication between the layout and its host
@@ -91,7 +91,7 @@ namespace Avalonia.Controls.Repeaters
         public abstract Size Arrange(LayoutContext context, Size finalSize);
 
         /// <summary>
-        /// Invalidates the measurement state (layout) for all IControl containers that reference
+        /// Invalidates the measurement state (layout) for all ILayoutable containers that reference
         /// this layout.
         /// </summary>
         protected void InvalidateMeasure() => MeasureInvalidated?.Invoke(this, EventArgs.Empty);

+ 22 - 23
src/Avalonia.Controls/Repeaters/ElementManager.cs → src/Avalonia.Layout/ElementManager.cs

@@ -6,14 +6,13 @@
 using System;
 using System.Collections.Generic;
 using System.Collections.Specialized;
-using System.Text;
-using Avalonia.Controls.Utils;
+using Avalonia.Layout.Utils;
 
-namespace Avalonia.Controls.Repeaters
+namespace Avalonia.Layout
 {
     internal class ElementManager
     {
-        private readonly List<IControl> _realizedElements = new List<IControl>();
+        private readonly List<ILayoutable> _realizedElements = new List<ILayoutable>();
         private readonly List<Rect> _realizedElementLayoutBounds = new List<Rect>();
         private int _firstRealizedDataIndex;
         private VirtualizingLayoutContext _context;
@@ -34,7 +33,7 @@ namespace Avalonia.Controls.Repeaters
 
         public void SetContext(VirtualizingLayoutContext virtualContext) => _context = virtualContext;
 
-        public void OnBeginMeasure(Orientation orientation)
+        public void OnBeginMeasure(ScrollOrientation orientation)
         {
             if (_context != null)
             {
@@ -69,9 +68,9 @@ namespace Avalonia.Controls.Repeaters
             return IsVirtualizingContext ? _realizedElements.Count : _context.ItemCount;
         }
 
-        public IControl GetAt(int realizedIndex)
+        public ILayoutable GetAt(int realizedIndex)
         {
-            IControl element;
+            ILayoutable element;
 
             if (IsVirtualizingContext)
             {
@@ -100,7 +99,7 @@ namespace Avalonia.Controls.Repeaters
             return element;
         }
 
-        public void Add(IControl element, int dataIndex)
+        public void Add(ILayoutable element, int dataIndex)
         {
             if (_realizedElements.Count == 0)
             {
@@ -111,7 +110,7 @@ namespace Avalonia.Controls.Repeaters
             _realizedElementLayoutBounds.Add(default);
         }
 
-        public void Insert(int realizedIndex, int dataIndex, IControl element)
+        public void Insert(int realizedIndex, int dataIndex, ILayoutable element)
         {
             if (realizedIndex == 0)
             {
@@ -208,7 +207,7 @@ namespace Avalonia.Controls.Repeaters
 
         public bool IsIndexValidInData(int currentIndex) => currentIndex >= 0 && currentIndex < _context.ItemCount;
 
-        public IControl GetRealizedElement(int dataIndex)
+        public ILayoutable GetRealizedElement(int dataIndex)
         {
             return IsVirtualizingContext ?
                 GetAt(GetRealizedRangeIndexFromDataIndex(dataIndex)) : 
@@ -236,7 +235,7 @@ namespace Avalonia.Controls.Repeaters
             }
         }
 
-        public bool IsWindowConnected(in Rect window, Orientation orientation, bool scrollOrientationSameAsFlow)
+        public bool IsWindowConnected(in Rect window, ScrollOrientation orientation, bool scrollOrientationSameAsFlow)
         {
             bool intersects = false;
 
@@ -246,14 +245,14 @@ namespace Avalonia.Controls.Repeaters
                 var lastElementBounds = GetLayoutBoundsForRealizedIndex(GetRealizedElementCount() - 1);
 
                 var effectiveOrientation = scrollOrientationSameAsFlow ?
-                    (orientation == Orientation.Vertical ? Orientation.Horizontal : Orientation.Vertical) :
+                    (orientation == ScrollOrientation.Vertical ? ScrollOrientation.Horizontal : ScrollOrientation.Vertical) :
                     orientation;
 
 
-                var windowStart = effectiveOrientation == Orientation.Vertical ? window.Y : window.X;
-                var windowEnd = effectiveOrientation == Orientation.Vertical ? window.Y + window.Height : window.X + window.Width;
-                var firstElementStart = effectiveOrientation == Orientation.Vertical ? firstElementBounds.Y : firstElementBounds.X;
-                var lastElementEnd = effectiveOrientation == Orientation.Vertical ? lastElementBounds.Y + lastElementBounds.Height : lastElementBounds.X + lastElementBounds.Width;
+                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 &&
@@ -298,7 +297,7 @@ namespace Avalonia.Controls.Repeaters
             }
         }
 
-        public int GetElementDataIndex(IControl suggestedAnchor)
+        public int GetElementDataIndex(ILayoutable suggestedAnchor)
         {
             var it = _realizedElements.IndexOf(suggestedAnchor);
             return it != -1 ? GetDataIndexFromRealizedRangeIndex(it) : -1;
@@ -314,7 +313,7 @@ namespace Avalonia.Controls.Repeaters
             return IsVirtualizingContext ? dataIndex - _firstRealizedDataIndex : dataIndex;
         }
 
-        private void DiscardElementsOutsideWindow(in Rect window, Orientation orientation)
+        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
@@ -369,12 +368,12 @@ namespace Avalonia.Controls.Repeaters
             }
         }
 
-        private static bool Intersects(in Rect lhs, in Rect rhs, Orientation orientation)
+        private static bool Intersects(in Rect lhs, in Rect rhs, ScrollOrientation orientation)
         {
-            var lhsStart = orientation == Orientation.Vertical ? lhs.Y : lhs.X;
-            var lhsEnd = orientation == Orientation.Vertical ? lhs.Y + lhs.Height : lhs.X + lhs.Width;
-            var rhsStart = orientation == Orientation.Vertical ? rhs.Y : rhs.X;
-            var rhsEnd = orientation == Orientation.Vertical ? rhs.Y + rhs.Height : rhs.X + rhs.Width;
+            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;
         }

+ 7 - 7
src/Avalonia.Controls/Repeaters/FlowLayoutAlgorithm.cs → src/Avalonia.Layout/FlowLayoutAlgorithm.cs

@@ -6,7 +6,7 @@
 using System;
 using System.Collections.Specialized;
 
-namespace Avalonia.Controls.Repeaters
+namespace Avalonia.Layout
 {
     internal class FlowLayoutAlgorithm
     {
@@ -72,7 +72,7 @@ namespace Avalonia.Controls.Repeaters
             bool isWrapping,
             double minItemSpacing,
             double lineSpacing,
-            Orientation orientation,
+            ScrollOrientation orientation,
             string layoutId)
         {
             _orientation.ScrollOrientation = orientation;
@@ -135,7 +135,7 @@ namespace Avalonia.Controls.Repeaters
         }
 
         public Size MeasureElement(
-            IControl element,
+            ILayoutable element,
             int index,
             Size availableSize,
             VirtualizingLayoutContext context)
@@ -469,9 +469,9 @@ namespace Avalonia.Controls.Repeaters
 
         private Rect EstimateExtent(Size availableSize, string layoutId)
         {
-            IControl firstRealizedElement = null;
+            ILayoutable firstRealizedElement = null;
             Rect firstBounds = new Rect();
-            IControl lastRealizedElement = null;
+            ILayoutable lastRealizedElement = null;
             Rect lastBounds = new Rect();
             int firstDataIndex = -1;
             int lastDataIndex = -1;
@@ -667,7 +667,7 @@ namespace Avalonia.Controls.Repeaters
             }
         }
 
-        public IControl GetElementIfRealized(int dataIndex)
+        public ILayoutable GetElementIfRealized(int dataIndex)
         {
             if (_elementManager.IsDataIndexRealized(dataIndex))
             {
@@ -677,7 +677,7 @@ namespace Avalonia.Controls.Repeaters
             return null;
         }
 
-        public bool TryAddElement0(IControl element)
+        public bool TryAddElement0(ILayoutable element)
         {
             if (_elementManager.GetRealizedElementCount() == 0)
             {

+ 4 - 8
src/Avalonia.Controls/Repeaters/IFlowLayoutAlgorithmDelegates.cs → src/Avalonia.Layout/IFlowLayoutAlgorithmDelegates.cs

@@ -3,11 +3,7 @@
 //
 // Licensed to The Avalonia Project under MIT License, courtesy of The .NET Foundation.
 
-using System;
-using System.Collections.Generic;
-using System.Text;
-
-namespace Avalonia.Controls.Repeaters
+namespace Avalonia.Layout
 {
     internal struct FlowLayoutAnchorInfo
     {
@@ -25,14 +21,14 @@ namespace Avalonia.Controls.Repeaters
         Rect Algorithm_GetExtent(
             Size availableSize,
             VirtualizingLayoutContext context,
-            IControl firstRealized,
+            ILayoutable firstRealized,
             int firstRealizedItemIndex,
             Rect firstRealizedLayoutBounds,
-            IControl lastRealized,
+            ILayoutable lastRealized,
             int lastRealizedItemIndex,
             Rect lastRealizedLayoutBounds);
         void Algorithm_OnElementMeasured(
-            IControl element,
+            ILayoutable element,
             int index,
             Size availableSize,
             Size measureSize,

+ 1 - 1
src/Avalonia.Controls/Repeaters/LayoutContext.cs → src/Avalonia.Layout/LayoutContext.cs

@@ -3,7 +3,7 @@
 //
 // Licensed to The Avalonia Project under MIT License, courtesy of The .NET Foundation.
 
-namespace Avalonia.Controls.Repeaters
+namespace Avalonia.Layout
 {
     /// <summary>
     /// Represents the base class for an object that facilitates communication between an attached

+ 2 - 2
src/Avalonia.Controls/Repeaters/NonVirtualizingLayout.cs → src/Avalonia.Layout/NonVirtualizingLayout.cs

@@ -3,13 +3,13 @@
 //
 // Licensed to The Avalonia Project under MIT License, courtesy of The .NET Foundation.
 
-namespace Avalonia.Controls.Repeaters
+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>
-    public abstract class NonVirtualizingLayout : Layout
+    public abstract class NonVirtualizingLayout : AttachedLayout
     {
     }
 }

+ 1 - 1
src/Avalonia.Controls/Orientation.cs → src/Avalonia.Layout/Orientation.cs

@@ -1,7 +1,7 @@
 // Copyright (c) The Avalonia Project. All rights reserved.
 // Licensed under the MIT license. See licence.md file in the project root for full license information.
 
-namespace Avalonia.Controls
+namespace Avalonia.Layout
 {
     /// <summary>
     /// Defines vertical or horizontal orientation.

+ 23 - 17
src/Avalonia.Controls/Repeaters/OrientationBasedMeasures.cs → src/Avalonia.Layout/OrientationBasedMeasures.cs

@@ -3,24 +3,30 @@
 //
 // Licensed to The Avalonia Project under MIT License, courtesy of The .NET Foundation.
 
-namespace Avalonia.Controls.Repeaters
+namespace Avalonia.Layout
 {
+    internal enum ScrollOrientation
+    {
+        Vertical,
+        Horizontal,
+    }
+
     internal class OrientationBasedMeasures
     {
-        public Orientation ScrollOrientation { get; set; } = Orientation.Vertical;
+        public ScrollOrientation ScrollOrientation { get; set; } = ScrollOrientation.Vertical;
 
-        public double Major(in Size size) => ScrollOrientation == Orientation.Vertical ? size.Height : size.Width;
-        public double Minor(in Size size) => ScrollOrientation == Orientation.Vertical ? size.Width : size.Height;
-        public double MajorSize(in Rect rect) => ScrollOrientation == Orientation.Vertical ? rect.Height : rect.Width;
-        public double MinorSize(in Rect rect) => ScrollOrientation == Orientation.Vertical ? rect.Width : rect.Height;
-        public double MajorStart(in Rect rect) => ScrollOrientation == Orientation.Vertical ? rect.Y : rect.X;
-        public double MinorStart(in Rect rect) => ScrollOrientation == Orientation.Vertical ? rect.X : rect.Y;
-        public double MajorEnd(in Rect rect) => ScrollOrientation == Orientation.Vertical ? rect.Bottom : rect.Right;
-        public double MinorEnd(in Rect rect) => ScrollOrientation == Orientation.Vertical ? rect.Right : rect.Bottom;
+        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 == Orientation.Vertical)
+            if (ScrollOrientation == ScrollOrientation.Vertical)
             {
                 rect = rect.WithHeight(value);
             }
@@ -32,7 +38,7 @@ namespace Avalonia.Controls.Repeaters
 
         public void SetMinorSize(ref Rect rect, double value)
         {
-            if (ScrollOrientation == Orientation.Vertical)
+            if (ScrollOrientation == ScrollOrientation.Vertical)
             {
                 rect = rect.WithWidth(value);
             }
@@ -44,7 +50,7 @@ namespace Avalonia.Controls.Repeaters
 
         public void SetMajorStart(ref Rect rect, double value)
         {
-            if (ScrollOrientation == Orientation.Vertical)
+            if (ScrollOrientation == ScrollOrientation.Vertical)
             {
                 rect = rect.WithY(value);
             }
@@ -56,7 +62,7 @@ namespace Avalonia.Controls.Repeaters
 
         public void SetMinorStart(ref Rect rect, double value)
         {
-            if (ScrollOrientation == Orientation.Vertical)
+            if (ScrollOrientation == ScrollOrientation.Vertical)
             {
                 rect = rect.WithX(value);
             }
@@ -68,21 +74,21 @@ namespace Avalonia.Controls.Repeaters
 
         public Rect MinorMajorRect(double minor, double major, double minorSize, double majorSize)
         {
-            return ScrollOrientation == Orientation.Vertical ?
+            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 == Orientation.Vertical ?
+            return ScrollOrientation == ScrollOrientation.Vertical ?
                 new Point(minor, major) :
                 new Point(major, minor);
         }
 
         public Size MinorMajorSize(double minor, double major)
         {
-            return ScrollOrientation == Orientation.Vertical ?
+            return ScrollOrientation == ScrollOrientation.Vertical ?
                 new Size(minor, major) :
                 new Size(major, minor);
         }

+ 10 - 10
src/Avalonia.Controls/Repeaters/StackLayout.cs → src/Avalonia.Layout/StackLayout.cs

@@ -6,7 +6,7 @@
 using System;
 using System.Collections.Specialized;
 
-namespace Avalonia.Controls.Repeaters
+namespace Avalonia.Layout
 {
     /// <summary>
     /// Arranges elements into a single line (with spacing) that can be oriented horizontally or vertically.
@@ -17,13 +17,13 @@ namespace Avalonia.Controls.Repeaters
         /// Defines the <see cref="Orientation"/> property.
         /// </summary>
         public static readonly StyledProperty<Orientation> OrientationProperty =
-            StackPanel.OrientationProperty.AddOwner<StackLayout>();
+            AvaloniaProperty.Register<StackLayout, Orientation>(nameof(Orientation), Orientation.Vertical);
 
         /// <summary>
         /// Defines the <see cref="Spacing"/> property.
         /// </summary>
         public static readonly StyledProperty<double> SpacingProperty =
-            StackPanel.SpacingProperty.AddOwner<StackLayout>();
+            AvaloniaProperty.Register<StackLayout, double>(nameof(Spacing));
 
         private readonly OrientationBasedMeasures _orientation = new OrientationBasedMeasures();
 
@@ -61,10 +61,10 @@ namespace Avalonia.Controls.Repeaters
         internal Rect GetExtent(
             Size availableSize,
             VirtualizingLayoutContext context,
-            IControl firstRealized,
+            ILayoutable firstRealized,
             int firstRealizedItemIndex,
             Rect firstRealizedLayoutBounds,
-            IControl lastRealized,
+            ILayoutable lastRealized,
             int lastRealizedItemIndex,
             Rect lastRealizedLayoutBounds)
         {
@@ -97,7 +97,7 @@ namespace Avalonia.Controls.Repeaters
         }
 
         internal void OnElementMeasured(
-            IControl element,
+            ILayoutable element,
             int index,
             Size availableSize,
             Size measureSize,
@@ -164,10 +164,10 @@ namespace Avalonia.Controls.Repeaters
         Rect IFlowLayoutAlgorithmDelegates.Algorithm_GetExtent(
             Size availableSize,
             VirtualizingLayoutContext context,
-            IControl firstRealized,
+            ILayoutable firstRealized,
             int firstRealizedItemIndex,
             Rect firstRealizedLayoutBounds,
-            IControl lastRealized,
+            ILayoutable lastRealized,
             int lastRealizedItemIndex,
             Rect lastRealizedLayoutBounds)
         {
@@ -182,7 +182,7 @@ namespace Avalonia.Controls.Repeaters
                 lastRealizedLayoutBounds);
         }
 
-        void IFlowLayoutAlgorithmDelegates.Algorithm_OnElementMeasured(IControl element, int index, Size availableSize, Size measureSize, Size desiredSize, Size provisionalArrangeSize, VirtualizingLayoutContext context)
+        void IFlowLayoutAlgorithmDelegates.Algorithm_OnElementMeasured(ILayoutable element, int index, Size availableSize, Size measureSize, Size desiredSize, Size provisionalArrangeSize, VirtualizingLayoutContext context)
         {
             OnElementMeasured(
                 element,
@@ -295,7 +295,7 @@ namespace Avalonia.Controls.Repeaters
             {
                 //Note: For StackLayout Vertical Orientation means we have a Vertical ScrollOrientation.
                 //Horizontal Orientation means we have a Horizontal ScrollOrientation.
-                _orientation.ScrollOrientation = (Orientation)e.NewValue;
+                _orientation.ScrollOrientation = (ScrollOrientation)e.NewValue;
             }
 
             InvalidateLayout();

+ 1 - 1
src/Avalonia.Controls/Repeaters/StackLayoutState.cs → src/Avalonia.Layout/StackLayoutState.cs

@@ -7,7 +7,7 @@ using System;
 using System.Collections.Generic;
 using System.Linq;
 
-namespace Avalonia.Controls.Repeaters
+namespace Avalonia.Layout
 {
     /// <summary>
     /// Represents the state of a StackLayout.

+ 10 - 10
src/Avalonia.Controls/Repeaters/UniformGridLayout.cs → src/Avalonia.Layout/UniformGridLayout.cs

@@ -6,7 +6,7 @@
 using System;
 using System.Collections.Specialized;
 
-namespace Avalonia.Controls.Repeaters
+namespace Avalonia.Layout
 {
     /// <summary>
     /// Defines constants that specify how items are aligned on the non-scrolling or non-virtualizing axis.
@@ -114,7 +114,7 @@ namespace Avalonia.Controls.Repeaters
         /// Defines the <see cref="Orientation"/> property.
         /// </summary>
         public static readonly StyledProperty<Orientation> OrientationProperty =
-            StackPanel.OrientationProperty.AddOwner<StackLayout>();
+            StackLayout.OrientationProperty.AddOwner<UniformGridLayout>();
 
         private readonly OrientationBasedMeasures _orientation = new OrientationBasedMeasures();
         private double _minItemWidth = double.NaN;
@@ -316,10 +316,10 @@ namespace Avalonia.Controls.Repeaters
         Rect IFlowLayoutAlgorithmDelegates.Algorithm_GetExtent(
             Size availableSize,
             VirtualizingLayoutContext context,
-            IControl firstRealized,
+            ILayoutable firstRealized,
             int firstRealizedItemIndex,
             Rect firstRealizedLayoutBounds,
-            IControl lastRealized,
+            ILayoutable lastRealized,
             int lastRealizedItemIndex,
             Rect lastRealizedLayoutBounds)
         {
@@ -359,7 +359,7 @@ namespace Avalonia.Controls.Repeaters
             return extent;
         }
 
-        void IFlowLayoutAlgorithmDelegates.Algorithm_OnElementMeasured(IControl element, int index, Size availableSize, Size measureSize, Size desiredSize, Size provisionalArrangeSize, VirtualizingLayoutContext context)
+        void IFlowLayoutAlgorithmDelegates.Algorithm_OnElementMeasured(ILayoutable element, int index, Size availableSize, Size measureSize, Size desiredSize, Size provisionalArrangeSize, VirtualizingLayoutContext context)
         {
         }
 
@@ -440,7 +440,7 @@ namespace Avalonia.Controls.Repeaters
 
                 //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) ? Orientation.Vertical : Orientation.Horizontal;
+                var scrollOrientation = (orientation == Orientation.Horizontal) ? ScrollOrientation.Vertical : ScrollOrientation.Horizontal;
                 _orientation.ScrollOrientation = scrollOrientation;
             }
             else if (args.Property == MinColumnSpacingProperty)
@@ -475,7 +475,7 @@ namespace Avalonia.Controls.Repeaters
         {
             var minItemSpacing = MinItemSpacing;
             var gridState = (UniformGridLayoutState)context.LayoutState;
-            return _orientation.ScrollOrientation == Orientation.Vertical?
+            return _orientation.ScrollOrientation == ScrollOrientation.Vertical?
                 gridState.EffectiveItemWidth + minItemSpacing :
                 gridState.EffectiveItemHeight + minItemSpacing;
         }
@@ -484,7 +484,7 @@ namespace Avalonia.Controls.Repeaters
         {
             var lineSpacing = LineSpacing;
             var gridState = (UniformGridLayoutState)context.LayoutState;
-            return _orientation.ScrollOrientation == Orientation.Vertical ?
+            return _orientation.ScrollOrientation == ScrollOrientation.Vertical ?
                 gridState.EffectiveItemHeight + lineSpacing :
                 gridState.EffectiveItemWidth + lineSpacing;
         }
@@ -503,8 +503,8 @@ namespace Avalonia.Controls.Repeaters
             Rect bounds = _orientation.MinorMajorRect(
                 indexInRow * GetMinorSizeWithSpacing(context) + _orientation.MinorStart(lastExtent),
                 rowIndex * GetMajorSizeWithSpacing(context) + _orientation.MajorStart(lastExtent),
-                _orientation.ScrollOrientation == Orientation.Vertical ? gridState.EffectiveItemWidth : gridState.EffectiveItemHeight,
-                _orientation.ScrollOrientation == Orientation.Vertical ? gridState.EffectiveItemHeight : gridState.EffectiveItemWidth);
+                _orientation.ScrollOrientation == ScrollOrientation.Vertical ? gridState.EffectiveItemWidth : gridState.EffectiveItemHeight,
+                _orientation.ScrollOrientation == ScrollOrientation.Vertical ? gridState.EffectiveItemHeight : gridState.EffectiveItemWidth);
 
             return bounds;
         }

+ 3 - 5
src/Avalonia.Controls/Repeaters/UniformGridLayoutState.cs → src/Avalonia.Layout/UniformGridLayoutState.cs

@@ -4,11 +4,9 @@
 // Licensed to The Avalonia Project under MIT License, courtesy of The .NET Foundation.
 
 using System;
-using System.Collections.Generic;
 using System.Collections.Specialized;
-using System.Text;
 
-namespace Avalonia.Controls.Repeaters
+namespace Avalonia.Layout
 {
     /// <summary>
     /// Represents the state of a <see cref="UniformGridLayout"/>.
@@ -20,7 +18,7 @@ namespace Avalonia.Controls.Repeaters
         // 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 IControl _cachedFirstElement;
+        private ILayoutable _cachedFirstElement;
 
         internal FlowLayoutAlgorithm FlowAlgorithm { get; } = new FlowLayoutAlgorithm();
         internal double EffectiveItemWidth { get; private set; }
@@ -86,7 +84,7 @@ namespace Avalonia.Controls.Repeaters
         }
 
         private void SetSize(
-            IControl element,
+            ILayoutable element,
             double layoutItemWidth,
             double LayoutItemHeight,
             Size availableSize,

+ 1 - 1
src/Avalonia.Controls/Utils/ListUtils.cs → src/Avalonia.Layout/Utils/ListUtils.cs

@@ -1,7 +1,7 @@
 using System.Collections.Generic;
 using System.Linq;
 
-namespace Avalonia.Controls.Utils
+namespace Avalonia.Layout.Utils
 {
     internal static class ListUtils
     {

+ 23 - 4
src/Avalonia.Controls/Repeaters/VirtualizingLayout.cs → src/Avalonia.Layout/VirtualizingLayout.cs

@@ -5,13 +5,13 @@
 
 using System.Collections.Specialized;
 
-namespace Avalonia.Controls.Repeaters
+namespace Avalonia.Layout
 {
     /// <summary>
     /// Represents the base class for an object that sizes and arranges child elements for a host
     /// and supports virtualization.
     /// </summary>
-    public abstract class VirtualizingLayout : Layout
+    public abstract class VirtualizingLayout : AttachedLayout
     {
         /// <inheritdoc/>
         public sealed override void InitializeForContext(LayoutContext context)
@@ -37,9 +37,28 @@ namespace Avalonia.Controls.Repeaters
             return ArrangeOverride((VirtualizingLayoutContext)context, 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>
+        /// <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 IControl container.
+        /// 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
@@ -51,7 +70,7 @@ namespace Avalonia.Controls.Repeaters
 
         /// <summary>
         /// When overridden in a derived class, removes any state the layout previously stored on
-        /// the IControl container.
+        /// the ILayoutable container.
         /// </summary>
         /// <param name="context">
         /// The context object that facilitates communication between the layout and its host

+ 7 - 7
src/Avalonia.Controls/Repeaters/VirtualizingLayoutContext.cs → src/Avalonia.Layout/VirtualizingLayoutContext.cs

@@ -5,7 +5,7 @@
 
 using System;
 
-namespace Avalonia.Controls.Repeaters
+namespace Avalonia.Layout
 {
     /// <summary>
     /// Defines constants that specify whether to suppress automatic recycling of the retrieved
@@ -114,7 +114,7 @@ namespace Avalonia.Controls.Repeaters
         /// This method calls <see cref="GetOrCreateElementAtCore(int, ElementRealizationOptions)"/>
         /// with options set to None. GetElementAtCore must be implemented in a derived class.
         /// </remarks>
-        public IControl GetOrCreateElementAt(int index)
+        public ILayoutable GetOrCreateElementAt(int index)
             => GetOrCreateElementAtCore(index, ElementRealizationOptions.None);
 
         /// <summary>
@@ -138,7 +138,7 @@ namespace Avalonia.Controls.Repeaters
         /// advanced layouts that choose to explicitly manage the realization and recycling of
         /// elements as a performance optimization.
         /// </remarks>
-        public IControl GetOrCreateElementAt(int index, ElementRealizationOptions options)
+        public ILayoutable GetOrCreateElementAt(int index, ElementRealizationOptions options)
             => GetOrCreateElementAtCore(index, options);
 
         /// <summary>
@@ -146,10 +146,10 @@ namespace Avalonia.Controls.Repeaters
         /// </summary>
         /// <param name="element">The element to clear.</param>
         /// <remarks>
-        /// This method calls <see cref="RecycleElementCore(IControl)"/>, which must be implemented
+        /// This method calls <see cref="RecycleElementCore(ILayoutable)"/>, which must be implemented
         /// in a derived class.
         /// </remarks>
-        public void RecycleElement(IControl element) => RecycleElementCore(element);
+        public void RecycleElement(ILayoutable element) => RecycleElementCore(element);
 
         /// <summary>
         /// When implemented in a derived class, retrieves the number of items in the data.
@@ -178,13 +178,13 @@ namespace Avalonia.Controls.Repeaters
         /// 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 IControl GetOrCreateElementAtCore(int index, ElementRealizationOptions options);
+        protected abstract ILayoutable 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(IControl element);
+        protected abstract void RecycleElementCore(ILayoutable element);
     }
 }

+ 1 - 0
tests/Avalonia.Base.UnitTests/Data/DefaultValueConverterTests.cs

@@ -8,6 +8,7 @@ using Xunit;
 using System.Windows.Input;
 using System;
 using Avalonia.Data.Converters;
+using Avalonia.Layout;
 
 namespace Avalonia.Base.UnitTests.Data.Converters
 {

+ 2 - 1
tests/Avalonia.Controls.UnitTests/Primitives/RangeBaseTests.cs

@@ -6,6 +6,7 @@ using System.ComponentModel;
 using Avalonia.Controls.Primitives;
 using Avalonia.Controls.Templates;
 using Avalonia.Data;
+using Avalonia.Layout;
 using Avalonia.Markup.Data;
 using Avalonia.Styling;
 using Xunit;
@@ -199,4 +200,4 @@ namespace Avalonia.Controls.UnitTests.Primitives
             }
         }
     }
-}
+}

+ 1 - 0
tests/Avalonia.Controls.UnitTests/Primitives/TrackTests.cs

@@ -2,6 +2,7 @@
 // Licensed under the MIT license. See licence.md file in the project root for full license information.
 
 using Avalonia.Controls.Primitives;
+using Avalonia.Layout;
 using Avalonia.LogicalTree;
 using Xunit;
 

+ 2 - 1
tests/Avalonia.Controls.UnitTests/ScrollViewerTests.cs

@@ -6,6 +6,7 @@ using System.Collections.Generic;
 using Avalonia.Controls.Presenters;
 using Avalonia.Controls.Primitives;
 using Avalonia.Controls.Templates;
+using Avalonia.Layout;
 using Xunit;
 
 namespace Avalonia.Controls.UnitTests
@@ -113,4 +114,4 @@ namespace Avalonia.Controls.UnitTests
             };
         }
     }
-}
+}

+ 1 - 0
tests/Avalonia.Controls.UnitTests/SliderTests.cs

@@ -1,6 +1,7 @@
 using System;
 using System.Collections.Generic;
 using System.Text;
+using Avalonia.Layout;
 using Xunit;
 
 namespace Avalonia.Controls.UnitTests

+ 2 - 1
tests/Avalonia.Controls.UnitTests/WrapPanelTests.cs

@@ -1,6 +1,7 @@
 // Copyright (c) The Avalonia Project. All rights reserved.
 // Licensed under the MIT license. See licence.md file in the project root for full license information.
 
+using Avalonia.Layout;
 using Xunit;
 
 namespace Avalonia.Controls.UnitTests
@@ -93,4 +94,4 @@ namespace Avalonia.Controls.UnitTests
             Assert.Equal(new Rect(100, 0, 100, 50), target.Children[1].Bounds);
         }
     }
-}
+}

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

@@ -1,4 +1,5 @@
 using Avalonia.Controls;
+using Avalonia.Layout;
 using Avalonia.UnitTests;
 using Xunit;
 

+ 1 - 0
tests/Avalonia.Visuals.UnitTests/VisualTree/VisualExtensions_GetVisualsAt.cs

@@ -1,6 +1,7 @@
 using System;
 using System.Linq;
 using Avalonia.Controls;
+using Avalonia.Layout;
 using Avalonia.Media;
 using Avalonia.Rendering;
 using Avalonia.UnitTests;