// 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 System; using System.Collections; using System.Collections.Specialized; using System.Reactive.Linq; using Avalonia.Controls.Primitives; using Avalonia.Controls.Utils; using Avalonia.Input; namespace Avalonia.Controls.Presenters { /// /// Base class for classes which handle virtualization for an . /// internal abstract class ItemVirtualizer : IVirtualizingController, IDisposable { private double _crossAxisOffset; private IDisposable _subscriptions; /// /// Initializes a new instance of the class. /// /// public ItemVirtualizer(ItemsPresenter owner) { Owner = owner; Items = owner.Items; ItemCount = owner.Items.Count(); var panel = VirtualizingPanel; if (panel != null) { _subscriptions = panel.GetObservable(Panel.BoundsProperty) .Skip(1) .Subscribe(_ => InvalidateScroll()); } } /// /// Gets the which owns the virtualizer. /// public ItemsPresenter Owner { get; } /// /// Gets the which will host the items. /// public IVirtualizingPanel VirtualizingPanel => Owner.Panel as IVirtualizingPanel; /// /// Gets the items to display. /// public IEnumerable Items { get; private set; } /// /// Gets the number of items in . /// public int ItemCount { get; private set; } /// /// Gets or sets the index of the first item displayed in the panel. /// public int FirstIndex { get; protected set; } /// /// Gets or sets the index of the first item beyond those displayed in the panel. /// public int NextIndex { get; protected set; } /// /// Gets a value indicating whether the items should be scroll horizontally or vertically. /// public bool Vertical => VirtualizingPanel?.ScrollDirection == Orientation.Vertical; /// /// Gets a value indicating whether logical scrolling is enabled. /// public abstract bool IsLogicalScrollEnabled { get; } /// /// Gets the value of the scroll extent. /// public abstract double ExtentValue { get; } /// /// Gets or sets the value of the current scroll offset. /// public abstract double OffsetValue { get; set; } /// /// Gets the value of the scrollable viewport. /// public abstract double ViewportValue { get; } /// /// Gets the as a . /// public Size Extent { get { return Vertical ? new Size(Owner.Panel.DesiredSize.Width, ExtentValue) : new Size(ExtentValue, Owner.Panel.DesiredSize.Height); } } /// /// Gets the as a . /// public Size Viewport { get { return Vertical ? new Size(Owner.Panel.Bounds.Width, ViewportValue) : new Size(ViewportValue, Owner.Panel.Bounds.Height); } } /// /// Gets or sets the as a . /// public Vector Offset { get { return Vertical ? new Vector(_crossAxisOffset, OffsetValue) : new Vector(OffsetValue, _crossAxisOffset); } set { var oldCrossAxisOffset = _crossAxisOffset; if (Vertical) { OffsetValue = value.Y; _crossAxisOffset = value.X; } else { OffsetValue = value.X; _crossAxisOffset = value.Y; } if (_crossAxisOffset != oldCrossAxisOffset) { Owner.InvalidateArrange(); } } } /// /// Creates an based on an item presenter's /// . /// /// The items presenter. /// An . public static ItemVirtualizer Create(ItemsPresenter owner) { if (owner.Panel == null) { return null; } var virtualizingPanel = owner.Panel as IVirtualizingPanel; var scrollable = (ILogicalScrollable)owner; ItemVirtualizer result = null; if (virtualizingPanel != null && scrollable.InvalidateScroll != null) { switch (owner.VirtualizationMode) { case ItemVirtualizationMode.Simple: result = new ItemVirtualizerSimple(owner); break; } } if (result == null) { result = new ItemVirtualizerNone(owner); } if (virtualizingPanel != null) { virtualizingPanel.Controller = result; } return result; } /// /// Carries out a measure for the related . /// /// The size available to the control. /// The desired size for the control. public virtual Size MeasureOverride(Size availableSize) { Owner.Panel.Measure(availableSize); return Owner.Panel.DesiredSize; } /// /// Carries out an arrange for the related . /// /// The size available to the control. /// The actual size used. public virtual Size ArrangeOverride(Size finalSize) { if (VirtualizingPanel != null) { VirtualizingPanel.CrossAxisOffset = _crossAxisOffset; Owner.Panel.Arrange(new Rect(finalSize)); } else { var origin = Vertical ? new Point(-_crossAxisOffset, 0) : new Point(0, _crossAxisOffset); Owner.Panel.Arrange(new Rect(origin, finalSize)); } return finalSize; } /// public virtual void UpdateControls() { } /// /// Gets the next control in the specified direction. /// /// The movement direction. /// The control from which movement begins. /// The control. public virtual IControl GetControlInDirection(NavigationDirection direction, IControl from) { return null; } /// /// Called when the items for the presenter change, either because /// has been set, the items collection has been /// modified, or the panel has been created. /// /// The items. /// A description of the change. public virtual void ItemsChanged(IEnumerable items, NotifyCollectionChangedEventArgs e) { Items = items; ItemCount = items.Count(); } /// /// Scrolls the specified item into view. /// /// The item. public virtual void ScrollIntoView(object item) { } /// public virtual void Dispose() { _subscriptions?.Dispose(); _subscriptions = null; if (VirtualizingPanel != null) { VirtualizingPanel.Controller = null; VirtualizingPanel.Children.Clear(); } Owner.ItemContainerGenerator.Clear(); } /// /// Invalidates the current scroll. /// protected void InvalidateScroll() => ((ILogicalScrollable)Owner).InvalidateScroll(); } }