Browse Source

Added pixel offset to virtualizing panel.

Steven Kirk 9 years ago
parent
commit
63a688817e

+ 4 - 0
src/Avalonia.Controls/IVirtualizingPanel.cs

@@ -8,6 +8,10 @@ namespace Avalonia.Controls
 
         int OverflowCount { get; }
 
+        double AverageItemSize { get; }
+
+        double PixelOffset { get; set; }
+
         Action ArrangeCompleted { get; set; }
     }
 }

+ 8 - 4
src/Avalonia.Controls/Presenters/ThingamybobPresenter.cs

@@ -17,6 +17,7 @@ namespace Avalonia.Controls.Presenters
                 _panel = new VirtualizingStackPanel();
                 _panel.ArrangeCompleted = CheckPanel;
                 Child = _panel;
+                CheckPanel();
             }
         }
 
@@ -24,28 +25,31 @@ namespace Avalonia.Controls.Presenters
 
         Action IScrollable.InvalidateScroll { get; set; }
 
-        Size IScrollable.Extent => new Size(1, 100);
+        Size IScrollable.Extent => new Size(1, 100 * AverageItemSize );
 
         Vector IScrollable.Offset
         {
             get
             {
-                return new Vector(0, _firstIndex);
+                return new Vector(0, _firstIndex * AverageItemSize);
             }
 
             set
             {
                 var count = _lastIndex - _firstIndex;
-                _firstIndex = (int)Math.Round(value.Y);
+                _firstIndex = (int)(value.Y / AverageItemSize);
                 _lastIndex = _firstIndex + count;
+                _panel.PixelOffset = value.Y % AverageItemSize;
                 Renumber();
             }
         }
 
-        Size IScrollable.Viewport => new Size(1, _lastIndex - _firstIndex);
+        Size IScrollable.Viewport => new Size(1, (_lastIndex - _firstIndex) * AverageItemSize);
         Size IScrollable.ScrollSize => new Size(0, 1);
         Size IScrollable.PageScrollSize => new Size(0, 1);
 
+        private double AverageItemSize => _panel?.AverageItemSize ?? 1;
+
         protected override Size ArrangeOverride(Size finalSize)
         {
             var result = base.ArrangeOverride(finalSize);

+ 67 - 18
src/Avalonia.Controls/VirtualizingStackPanel.cs

@@ -1,7 +1,6 @@
 // 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.Controls.Primitives;
 using System;
 using System.Collections.Specialized;
 
@@ -11,6 +10,9 @@ namespace Avalonia.Controls
     {
         private double _takenSpace;
         private int _canBeRemoved;
+        private double _averageItemSize;
+        private int _averageCount;
+        private double _pixelOffset;
 
         bool IVirtualizingPanel.IsFull
         {
@@ -24,12 +26,30 @@ namespace Avalonia.Controls
 
         int IVirtualizingPanel.OverflowCount => _canBeRemoved;
 
+        double IVirtualizingPanel.AverageItemSize => _averageItemSize;
+
+        double IVirtualizingPanel.PixelOffset
+        {
+            get { return _pixelOffset; }
+
+            set
+            {
+                if (_pixelOffset != value)
+                {
+                    _pixelOffset = value;
+                    InvalidateArrange();
+                }
+            }
+        }
+
         Action IVirtualizingPanel.ArrangeCompleted { get; set; }
 
         protected override Size ArrangeOverride(Size finalSize)
         {
             _canBeRemoved = 0;
             _takenSpace = 0;
+            _averageItemSize = 0;
+            _averageCount = 0;
             var result = base.ArrangeOverride(finalSize);
             ((IVirtualizingPanel)this).ArrangeCompleted?.Invoke();
             return result;
@@ -44,7 +64,7 @@ namespace Avalonia.Controls
                 case NotifyCollectionChangedAction.Add:
                     foreach (IControl control in e.NewItems)
                     {
-                        UpdatePhysicalSizeForAdd(control);
+                        UpdateAdd(control);
                     }
 
                     break;
@@ -52,7 +72,7 @@ namespace Avalonia.Controls
                 case NotifyCollectionChangedAction.Remove:
                     foreach (IControl control in e.OldItems)
                     {
-                        UpdatePhysicalSizeForRemove(control);
+                        UpdateRemove(control);
                     }
 
                     break;
@@ -65,64 +85,93 @@ namespace Avalonia.Controls
             Size panelSize,
             Orientation orientation)
         {
-            base.ArrangeChild(child, rect, panelSize, orientation);
-
-            if (orientation == Orientation.Horizontal)
+            if (orientation == Orientation.Vertical)
             {
-                if (rect.X >= panelSize.Width)
+                rect = new Rect(rect.X, rect.Y - _pixelOffset, rect.Width, rect.Height);
+                child.Arrange(rect);
+
+                if (rect.Y >= panelSize.Height)
                 {
                     ++_canBeRemoved;
                 }
 
-                if (rect.Right >= _takenSpace)
+                if (rect.Bottom >= _takenSpace)
                 {
-                    _takenSpace = rect.Right;
+                    _takenSpace = rect.Bottom;
                 }
+
+                AddToAverageItemSize(rect.Height);
             }
             else
             {
-                if (rect.Y >= panelSize.Height)
+                rect = new Rect(rect.X - _pixelOffset, rect.Y, rect.Width, rect.Height);
+                child.Arrange(rect);
+
+                if (rect.X >= panelSize.Width)
                 {
                     ++_canBeRemoved;
                 }
 
-                if (rect.Bottom >= _takenSpace)
+                if (rect.Right >= _takenSpace)
                 {
-                    _takenSpace = rect.Bottom;
+                    _takenSpace = rect.Right;
                 }
+
+                AddToAverageItemSize(rect.Width);
             }
         }
 
-        private void UpdatePhysicalSizeForAdd(IControl child)
+        private void UpdateAdd(IControl child)
         {
             var bounds = Bounds;
             var gap = Gap;
 
             child.Measure(bounds.Size);
+            ++_averageCount;
 
             if (Orientation == Orientation.Vertical)
             {
-                _takenSpace += child.DesiredSize.Height + gap;
+                var height = child.DesiredSize.Height;
+                _takenSpace += height + gap;
+                AddToAverageItemSize(height);
             }
             else
             {
-                _takenSpace += child.DesiredSize.Width + gap;
+                var width = child.DesiredSize.Width;
+                _takenSpace += width + gap;
+                AddToAverageItemSize(width);
             }
         }
 
-        private void UpdatePhysicalSizeForRemove(IControl child)
+        private void UpdateRemove(IControl child)
         {
             var bounds = Bounds;
             var gap = Gap;
 
             if (Orientation == Orientation.Vertical)
             {
-                _takenSpace -= child.DesiredSize.Height + gap;
+                var height = child.DesiredSize.Height;
+                _takenSpace -= height + gap;
+                RemoveFromAverageItemSize(height);
             }
             else
             {
-                _takenSpace -= child.DesiredSize.Width + gap;
+                var width = child.DesiredSize.Width;
+                _takenSpace -= width + gap;
+                RemoveFromAverageItemSize(width);
             }
         }
+
+        private void AddToAverageItemSize(double value)
+        {
+            ++_averageCount;
+            _averageItemSize += (value - _averageItemSize) / _averageCount;
+        }
+
+        private void RemoveFromAverageItemSize(double value)
+        {
+            _averageItemSize = ((_averageItemSize * _averageCount) - value) / (_averageCount - 1);
+            --_averageCount;
+        }
     }
 }