using System;
using System.Collections.Generic;
using System.Collections.Specialized;
using System.Diagnostics;
using System.Threading;
using System.Threading.Tasks;
using Avalonia.Animation;
using Avalonia.Controls.Primitives;
using Avalonia.Input;
namespace Avalonia.Controls
{
///
/// A panel used by to display the current item.
///
public class VirtualizingCarouselPanel : VirtualizingPanel, ILogicalScrollable
{
private static readonly AttachedProperty ItemIsOwnContainerProperty =
AvaloniaProperty.RegisterAttached("ItemIsOwnContainer");
private Size _extent;
private Vector _offset;
private Size _viewport;
private Stack? _recyclePool;
private Control? _realized;
private int _realizedIndex = -1;
private Control? _transitionFrom;
private int _transitionFromIndex = -1;
private CancellationTokenSource? _transition;
private EventHandler? _scrollInvalidated;
bool ILogicalScrollable.CanHorizontallyScroll { get; set; }
bool ILogicalScrollable.CanVerticallyScroll { get; set; }
bool ILogicalScrollable.IsLogicalScrollEnabled => true;
Size ILogicalScrollable.ScrollSize => new(1, 1);
Size ILogicalScrollable.PageScrollSize => new(1, 1);
Size IScrollable.Extent => Extent;
Size IScrollable.Viewport => Viewport;
Vector IScrollable.Offset
{
get => _offset;
set
{
if ((int)_offset.X != value.X)
InvalidateMeasure();
_offset = value;
}
}
private Size Extent
{
get => _extent;
set
{
if (_extent != value)
{
_extent = value;
_scrollInvalidated?.Invoke(this, EventArgs.Empty);
}
}
}
private Size Viewport
{
get => _viewport;
set
{
if (_viewport != value)
{
_viewport = value;
_scrollInvalidated?.Invoke(this, EventArgs.Empty);
}
}
}
event EventHandler? ILogicalScrollable.ScrollInvalidated
{
add => _scrollInvalidated += value;
remove => _scrollInvalidated -= value;
}
bool ILogicalScrollable.BringIntoView(Control target, Rect targetRect) => false;
Control? ILogicalScrollable.GetControlInDirection(NavigationDirection direction, Control? from) => null;
void ILogicalScrollable.RaiseScrollInvalidated(EventArgs e) => _scrollInvalidated?.Invoke(this, e);
protected override Size MeasureOverride(Size availableSize)
{
var items = Items;
var index = (int)_offset.X;
if (index != _realizedIndex)
{
if (_realized is not null)
{
var cancelTransition = _transition is not null;
// Cancel any already running transition, and recycle the element we're transitioning from.
if (cancelTransition)
{
_transition!.Cancel();
_transition = null;
if (_transitionFrom is not null)
RecycleElement(_transitionFrom);
_transitionFrom = null;
_transitionFromIndex = -1;
}
if (cancelTransition || GetTransition() is null)
{
// If don't have a transition or we've just canceled a transition then recycle the element
// we're moving from.
RecycleElement(_realized);
}
else
{
// We have a transition to do: record the current element as the element we're transitioning
// from and we'll start the transition in the arrange pass.
_transitionFrom = _realized;
_transitionFromIndex = _realizedIndex;
}
_realized = null;
_realizedIndex = -1;
}
// Get or create an element for the new item.
if (index >= 0 && index < items.Count)
{
_realized = GetOrCreateElement(items, index);
_realizedIndex = index;
}
}
if (_realized is null)
{
Extent = Viewport = new(0, 0);
_transitionFrom = null;
_transitionFromIndex = -1;
return default;
}
_realized.Measure(availableSize);
Extent = new(items.Count, 1);
Viewport = new(1, 1);
return _realized.DesiredSize;
}
protected override Size ArrangeOverride(Size finalSize)
{
var result = base.ArrangeOverride(finalSize);
if (_transition is null &&
_transitionFrom is not null &&
_realized is { } to &&
GetTransition() is { } transition)
{
_transition = new CancellationTokenSource();
transition.Start(_transitionFrom, to, _realizedIndex > _transitionFromIndex, _transition.Token)
.ContinueWith(TransitionFinished, TaskScheduler.FromCurrentSynchronizationContext());
}
return result;
}
protected override IInputElement? GetControl(NavigationDirection direction, IInputElement? from, bool wrap) => null;
protected internal override Control? ContainerFromIndex(int index)
{
return index == _realizedIndex ? _realized : null;
}
protected internal override IEnumerable? GetRealizedContainers()
{
return _realized is not null ? new[] { _realized } : null;
}
protected internal override int IndexFromContainer(Control container)
{
return container == _realized ? _realizedIndex : -1;
}
protected internal override Control? ScrollIntoView(int index)
{
return null;
}
protected override void OnItemsChanged(IReadOnlyList