Browse Source

WIP Trying to get virt scrolling working.

pull/554/head
Steven Kirk 10 years ago
parent
commit
1c7a99dc5e
  1. 1
      src/Avalonia.Controls/Presenters/ItemVirtualizer.cs
  2. 16
      src/Avalonia.Controls/Presenters/ItemVirtualizerNone.cs
  3. 69
      src/Avalonia.Controls/Presenters/ItemVirtualizerSimple.cs
  4. 21
      src/Avalonia.Controls/Presenters/ItemsPresenter.cs
  5. 62
      tests/Avalonia.Controls.UnitTests/Presenters/ItemsPresenterTests_Virtualization.cs

1
src/Avalonia.Controls/Presenters/ItemVirtualizer.cs

@ -23,6 +23,7 @@ namespace Avalonia.Controls.Presenters
public abstract bool IsLogicalScrollEnabled { get; }
public abstract Size Extent { get; }
public abstract Vector Offset { get; set; }
public abstract Size Viewport { get; }
public static ItemVirtualizer Create(ItemsPresenter owner)

16
src/Avalonia.Controls/Presenters/ItemVirtualizerNone.cs

@ -21,18 +21,18 @@ namespace Avalonia.Controls.Presenters
public override Size Extent
{
get
{
throw new NotSupportedException();
}
get { throw new NotSupportedException(); }
}
public override Vector Offset
{
get { throw new NotSupportedException(); }
set { throw new NotSupportedException(); }
}
public override Size Viewport
{
get
{
throw new NotSupportedException();
}
get { throw new NotSupportedException(); }
}
public override void Arranging(Size finalSize)

69
src/Avalonia.Controls/Presenters/ItemVirtualizerSimple.cs

@ -31,6 +31,75 @@ namespace Avalonia.Controls.Presenters
}
}
public override Vector Offset
{
get
{
if (VirtualizingPanel.ScrollDirection == Orientation.Vertical)
{
return new Vector(0, FirstIndex);
}
else
{
return new Vector(FirstIndex, 0);
}
}
set
{
var scroll = (VirtualizingPanel.ScrollDirection == Orientation.Vertical) ?
value.Y : value.X;
var delta = (int)(scroll - FirstIndex);
var panel = VirtualizingPanel;
if (delta != 0)
{
if (delta >= panel.Children.Count)
{
var index = FirstIndex + delta;
foreach (var container in panel.Children)
{
container.DataContext = Items.ElementAt(index++);
}
}
else if (delta > 0)
{
var containers = panel.Children.GetRange(0, delta).ToList();
panel.Children.RemoveRange(0, delta);
var index = LastIndex + 1;
foreach (var container in containers)
{
container.DataContext = Items.ElementAt(index++);
}
panel.Children.AddRange(containers);
}
else
{
var first = panel.Children.Count + delta;
var count = -delta;
var containers = panel.Children.GetRange(first, count).ToList();
panel.Children.RemoveRange(first, count);
var index = FirstIndex + delta;
foreach (var container in containers)
{
container.DataContext = Items.ElementAt(index++);
}
panel.Children.InsertRange(0, containers);
}
FirstIndex += delta;
LastIndex += delta;
}
}
}
public override Size Viewport
{
get

21
src/Avalonia.Controls/Presenters/ItemsPresenter.cs

@ -2,14 +2,10 @@
// Licensed under the MIT license. See licence.md file in the project root for full license information.
using System;
using System.Collections;
using System.Collections.Generic;
using System.Collections.Specialized;
using System.Linq;
using Avalonia.Controls.Generators;
using Avalonia.Controls.Primitives;
using Avalonia.Controls.Utils;
using Avalonia.Input;
using static Avalonia.Utilities.MathUtilities;
namespace Avalonia.Controls.Presenters
{
@ -60,7 +56,11 @@ namespace Avalonia.Controls.Presenters
Size IScrollable.Extent => _virtualizer.Extent;
/// <inheritdoc/>
Vector IScrollable.Offset { get; set; }
Vector IScrollable.Offset
{
get { return _virtualizer.Offset; }
set { _virtualizer.Offset = CoerceOffset(value); }
}
/// <inheritdoc/>
Size IScrollable.Viewport => _virtualizer.Viewport;
@ -83,6 +83,7 @@ namespace Avalonia.Controls.Presenters
protected override void PanelCreated(IPanel panel)
{
_virtualizer = ItemVirtualizer.Create(this);
((IScrollable)this).InvalidateScroll?.Invoke();
if (!Panel.IsSet(KeyboardNavigation.DirectionalNavigationProperty))
{
@ -100,5 +101,13 @@ namespace Avalonia.Controls.Presenters
{
_virtualizer?.ItemsChanged(Items, e);
}
private Vector CoerceOffset(Vector value)
{
var scrollable = (IScrollable)this;
var maxX = Math.Max(scrollable.Extent.Width - scrollable.Viewport.Width, 0);
var maxY = Math.Max(scrollable.Extent.Height - scrollable.Viewport.Height, 0);
return new Vector(Clamp(value.X, 0, maxX), Clamp(value.Y, 0, maxY));
}
}
}

62
tests/Avalonia.Controls.UnitTests/Presenters/ItemsPresenterTests_Virtualization.cs

@ -182,6 +182,68 @@ namespace Avalonia.Controls.UnitTests.Presenters
Assert.Equal(8, target.Panel.Children.Count);
}
[Fact]
public void Scrolling_Less_Than_A_Page_Should_Move_Recycled_Items()
{
var target = CreateTarget();
var items = (IList<string>)target.Items;
target.ApplyTemplate();
target.Measure(new Size(100, 100));
target.Arrange(new Rect(0, 0, 100, 100));
var containers = target.Panel.Children.ToList();
var scroller = (ScrollContentPresenter)target.Parent;
scroller.Offset = new Vector(0, 5);
var scrolledContainers = containers
.Skip(5)
.Take(5)
.Concat(containers.Take(5)).ToList();
Assert.Equal(new Vector(0, 5), ((IScrollable)target).Offset);
Assert.Equal(scrolledContainers, target.Panel.Children);
for (var i = 0; i < target.Panel.Children.Count; ++i)
{
Assert.Equal(items[i + 5], target.Panel.Children[i].DataContext);
}
scroller.Offset = new Vector(0, 0);
Assert.Equal(new Vector(0, 0), ((IScrollable)target).Offset);
Assert.Equal(containers, target.Panel.Children);
for (var i = 0; i < target.Panel.Children.Count; ++i)
{
Assert.Equal(items[i], target.Panel.Children[i].DataContext);
}
}
[Fact]
public void Scrolling_More_Than_A_Page_Should_Recycle_Items()
{
var target = CreateTarget();
var items = (IList<string>)target.Items;
target.ApplyTemplate();
target.Measure(new Size(100, 100));
target.Arrange(new Rect(0, 0, 100, 100));
var containers = target.Panel.Children.ToList();
var scroller = (ScrollContentPresenter)target.Parent;
scroller.Offset = new Vector(0, 10);
Assert.Equal(new Vector(0, 10), ((IScrollable)target).Offset);
Assert.Equal(containers, target.Panel.Children);
for (var i = 0; i < target.Panel.Children.Count; ++i)
{
Assert.Equal(items[i + 10], target.Panel.Children[i].DataContext);
}
}
}
private static ItemsPresenter CreateTarget(

Loading…
Cancel
Save