10 changed files with 993 additions and 1391 deletions
@ -1,276 +0,0 @@ |
|||||
using System.Collections.Specialized; |
|
||||
using System.Linq; |
|
||||
using System.Reactive.Linq; |
|
||||
using System.Threading.Tasks; |
|
||||
using Avalonia.Animation; |
|
||||
using Avalonia.Controls.Primitives; |
|
||||
using Avalonia.Controls.Utils; |
|
||||
using Avalonia.Data; |
|
||||
|
|
||||
namespace Avalonia.Controls.Presenters |
|
||||
{ |
|
||||
/// <summary>
|
|
||||
/// Displays pages inside an <see cref="ItemsControl"/>.
|
|
||||
/// </summary>
|
|
||||
public class CarouselPresenter : ItemsPresenter |
|
||||
{ |
|
||||
/// <summary>
|
|
||||
/// Defines the <see cref="IsVirtualized"/> property.
|
|
||||
/// </summary>
|
|
||||
public static readonly StyledProperty<bool> IsVirtualizedProperty = |
|
||||
Carousel.IsVirtualizedProperty.AddOwner<CarouselPresenter>(); |
|
||||
|
|
||||
/// <summary>
|
|
||||
/// Defines the <see cref="SelectedIndex"/> property.
|
|
||||
/// </summary>
|
|
||||
public static readonly DirectProperty<CarouselPresenter, int> SelectedIndexProperty = |
|
||||
SelectingItemsControl.SelectedIndexProperty.AddOwner<CarouselPresenter>( |
|
||||
o => o.SelectedIndex, |
|
||||
(o, v) => o.SelectedIndex = v); |
|
||||
|
|
||||
/// <summary>
|
|
||||
/// Defines the <see cref="PageTransition"/> property.
|
|
||||
/// </summary>
|
|
||||
public static readonly StyledProperty<IPageTransition?> PageTransitionProperty = |
|
||||
Carousel.PageTransitionProperty.AddOwner<CarouselPresenter>(); |
|
||||
|
|
||||
private int _selectedIndex = -1; |
|
||||
private Task? _currentTransition; |
|
||||
private int _queuedTransitionIndex = -1; |
|
||||
|
|
||||
/// <summary>
|
|
||||
/// Initializes static members of the <see cref="CarouselPresenter"/> class.
|
|
||||
/// </summary>
|
|
||||
static CarouselPresenter() |
|
||||
{ |
|
||||
////IsVirtualizedProperty.Changed.AddClassHandler<CarouselPresenter>((x, e) => x.IsVirtualizedChanged(e));
|
|
||||
////SelectedIndexProperty.Changed.AddClassHandler<CarouselPresenter>((x, e) => x.SelectedIndexChanged(e));
|
|
||||
} |
|
||||
|
|
||||
/// <summary>
|
|
||||
/// Gets or sets a value indicating whether the items in the carousel are virtualized.
|
|
||||
/// </summary>
|
|
||||
/// <remarks>
|
|
||||
/// When the carousel is virtualized, only the active page is held in memory.
|
|
||||
/// </remarks>
|
|
||||
public bool IsVirtualized |
|
||||
{ |
|
||||
get { return GetValue(IsVirtualizedProperty); } |
|
||||
set { SetValue(IsVirtualizedProperty, value); } |
|
||||
} |
|
||||
|
|
||||
/// <summary>
|
|
||||
/// Gets or sets the index of the selected page.
|
|
||||
/// </summary>
|
|
||||
public int SelectedIndex |
|
||||
{ |
|
||||
get |
|
||||
{ |
|
||||
return _selectedIndex; |
|
||||
} |
|
||||
|
|
||||
set |
|
||||
{ |
|
||||
////var old = SelectedIndex;
|
|
||||
////var effective = (value >= 0 && value < Items?.Cast<object>().Count()) ? value : -1;
|
|
||||
|
|
||||
////if (old != effective)
|
|
||||
////{
|
|
||||
//// _selectedIndex = effective;
|
|
||||
//// RaisePropertyChanged(SelectedIndexProperty, old, effective, BindingPriority.LocalValue);
|
|
||||
////}
|
|
||||
} |
|
||||
} |
|
||||
|
|
||||
/// <summary>
|
|
||||
/// Gets or sets a transition to use when switching pages.
|
|
||||
/// </summary>
|
|
||||
public IPageTransition? PageTransition |
|
||||
{ |
|
||||
get { return GetValue(PageTransitionProperty); } |
|
||||
set { SetValue(PageTransitionProperty, value); } |
|
||||
} |
|
||||
|
|
||||
/// <inheritdoc/>
|
|
||||
////protected override void ItemsChanged(NotifyCollectionChangedEventArgs e)
|
|
||||
////{
|
|
||||
//// if (!IsVirtualized)
|
|
||||
//// {
|
|
||||
//// base.ItemsChanged(e);
|
|
||||
|
|
||||
//// if (Items == null || SelectedIndex >= Items.Count())
|
|
||||
//// {
|
|
||||
//// SelectedIndex = Items.Count() - 1;
|
|
||||
//// }
|
|
||||
|
|
||||
//// foreach (var c in ItemContainerGenerator.Containers)
|
|
||||
//// {
|
|
||||
//// c.ContainerControl.IsVisible = c.Index == SelectedIndex;
|
|
||||
//// }
|
|
||||
//// }
|
|
||||
//// else if (SelectedIndex != -1 && Panel != null)
|
|
||||
//// {
|
|
||||
//// switch (e.Action)
|
|
||||
//// {
|
|
||||
//// case NotifyCollectionChangedAction.Add:
|
|
||||
//// if (e.NewStartingIndex > SelectedIndex)
|
|
||||
//// {
|
|
||||
//// return;
|
|
||||
//// }
|
|
||||
//// break;
|
|
||||
//// case NotifyCollectionChangedAction.Remove:
|
|
||||
//// if (e.OldStartingIndex > SelectedIndex)
|
|
||||
//// {
|
|
||||
//// return;
|
|
||||
//// }
|
|
||||
//// break;
|
|
||||
//// case NotifyCollectionChangedAction.Replace:
|
|
||||
//// if (e.OldStartingIndex > SelectedIndex ||
|
|
||||
//// e.OldStartingIndex + e.OldItems!.Count - 1 < SelectedIndex)
|
|
||||
//// {
|
|
||||
//// return;
|
|
||||
//// }
|
|
||||
//// break;
|
|
||||
//// case NotifyCollectionChangedAction.Move:
|
|
||||
//// if (e.OldStartingIndex > SelectedIndex &&
|
|
||||
//// e.NewStartingIndex > SelectedIndex)
|
|
||||
//// {
|
|
||||
//// return;
|
|
||||
//// }
|
|
||||
//// break;
|
|
||||
//// }
|
|
||||
|
|
||||
//// if (Items == null || SelectedIndex >= Items.Count())
|
|
||||
//// {
|
|
||||
//// SelectedIndex = Items.Count() - 1;
|
|
||||
//// }
|
|
||||
|
|
||||
//// Panel.Children.Clear();
|
|
||||
//// ItemContainerGenerator.Clear();
|
|
||||
|
|
||||
//// if (SelectedIndex != -1)
|
|
||||
//// {
|
|
||||
//// GetOrCreateContainer(SelectedIndex);
|
|
||||
//// }
|
|
||||
//// }
|
|
||||
////}
|
|
||||
|
|
||||
////protected override void PanelCreated(Panel panel)
|
|
||||
////{
|
|
||||
//// ItemsChanged(new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Reset));
|
|
||||
////}
|
|
||||
|
|
||||
/////// <summary>
|
|
||||
/////// Moves to the selected page, animating if a <see cref="PageTransition"/> is set.
|
|
||||
/////// </summary>
|
|
||||
/////// <param name="fromIndex">The index of the old page.</param>
|
|
||||
/////// <param name="toIndex">The index of the new page.</param>
|
|
||||
/////// <returns>A task tracking the animation.</returns>
|
|
||||
////private async Task MoveToPage(int fromIndex, int toIndex)
|
|
||||
////{
|
|
||||
//// if (fromIndex != toIndex)
|
|
||||
//// {
|
|
||||
//// var generator = ItemContainerGenerator;
|
|
||||
//// Control? from = null;
|
|
||||
//// Control? to = null;
|
|
||||
|
|
||||
//// if (fromIndex != -1)
|
|
||||
//// {
|
|
||||
//// from = generator.ContainerFromIndex(fromIndex);
|
|
||||
//// }
|
|
||||
|
|
||||
//// if (toIndex != -1)
|
|
||||
//// {
|
|
||||
//// to = GetOrCreateContainer(toIndex);
|
|
||||
//// }
|
|
||||
|
|
||||
//// if (PageTransition != null && (from != null || to != null))
|
|
||||
//// {
|
|
||||
//// await PageTransition.Start((Visual?)from, (Visual?)to, fromIndex < toIndex, default);
|
|
||||
//// }
|
|
||||
//// else if (to != null)
|
|
||||
//// {
|
|
||||
//// to.IsVisible = true;
|
|
||||
//// }
|
|
||||
|
|
||||
//// if (from != null)
|
|
||||
//// {
|
|
||||
//// if (IsVirtualized)
|
|
||||
//// {
|
|
||||
//// Panel!.Children.Remove(from);
|
|
||||
//// generator.Dematerialize(fromIndex, 1);
|
|
||||
//// }
|
|
||||
//// else
|
|
||||
//// {
|
|
||||
//// from.IsVisible = false;
|
|
||||
//// }
|
|
||||
//// }
|
|
||||
//// }
|
|
||||
////}
|
|
||||
|
|
||||
////private Control? GetOrCreateContainer(int index)
|
|
||||
////{
|
|
||||
//// var container = ItemContainerGenerator.ContainerFromIndex(index);
|
|
||||
|
|
||||
//// if (container == null && IsVirtualized)
|
|
||||
//// {
|
|
||||
//// var item = Items!.Cast<object>().ElementAt(index);
|
|
||||
//// var materialized = ItemContainerGenerator.Materialize(index, item);
|
|
||||
//// Panel!.Children.Add(materialized.ContainerControl);
|
|
||||
//// container = materialized.ContainerControl;
|
|
||||
//// }
|
|
||||
|
|
||||
//// return container;
|
|
||||
////}
|
|
||||
|
|
||||
/////// <summary>
|
|
||||
/////// Called when the <see cref="IsVirtualized"/> property changes.
|
|
||||
/////// </summary>
|
|
||||
/////// <param name="e">The event args.</param>
|
|
||||
////private void IsVirtualizedChanged(AvaloniaPropertyChangedEventArgs e)
|
|
||||
////{
|
|
||||
//// if (Panel != null)
|
|
||||
//// {
|
|
||||
//// ItemsChanged(new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Reset));
|
|
||||
//// }
|
|
||||
////}
|
|
||||
|
|
||||
/////// <summary>
|
|
||||
/////// Called when the <see cref="SelectedIndex"/> property changes.
|
|
||||
/////// </summary>
|
|
||||
/////// <param name="e">The event args.</param>
|
|
||||
////private async void SelectedIndexChanged(AvaloniaPropertyChangedEventArgs e)
|
|
||||
////{
|
|
||||
//// if (Panel != null)
|
|
||||
//// {
|
|
||||
//// if (_currentTransition == null)
|
|
||||
//// {
|
|
||||
//// int fromIndex = (int)e.OldValue!;
|
|
||||
//// int toIndex = (int)e.NewValue!;
|
|
||||
|
|
||||
//// for (;;)
|
|
||||
//// {
|
|
||||
//// _currentTransition = MoveToPage(fromIndex, toIndex);
|
|
||||
//// await _currentTransition;
|
|
||||
|
|
||||
//// if (_queuedTransitionIndex != -1)
|
|
||||
//// {
|
|
||||
//// fromIndex = toIndex;
|
|
||||
//// toIndex = _queuedTransitionIndex;
|
|
||||
//// _queuedTransitionIndex = -1;
|
|
||||
//// }
|
|
||||
//// else
|
|
||||
//// {
|
|
||||
//// _currentTransition = null;
|
|
||||
//// break;
|
|
||||
//// }
|
|
||||
//// }
|
|
||||
//// }
|
|
||||
//// else
|
|
||||
//// {
|
|
||||
//// _queuedTransitionIndex = (int)e.NewValue!;
|
|
||||
//// }
|
|
||||
//// }
|
|
||||
////}
|
|
||||
} |
|
||||
} |
|
||||
@ -0,0 +1,351 @@ |
|||||
|
using System; |
||||
|
using System.Collections; |
||||
|
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 |
||||
|
{ |
||||
|
/// <summary>
|
||||
|
/// A panel used by <see cref="Carousel"/> to display the current item.
|
||||
|
/// </summary>
|
||||
|
public class VirtualizingCarouselPanel : VirtualizingPanel, ILogicalScrollable |
||||
|
{ |
||||
|
private static readonly AttachedProperty<bool> ItemIsOwnContainerProperty = |
||||
|
AvaloniaProperty.RegisterAttached<VirtualizingCarouselPanel, Control, bool>("ItemIsOwnContainer"); |
||||
|
|
||||
|
private Size _extent; |
||||
|
private Vector _offset; |
||||
|
private Size _viewport; |
||||
|
private Stack<Control>? _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) |
||||
|
{ |
||||
|
throw new NotImplementedException(); |
||||
|
} |
||||
|
|
||||
|
Control? ILogicalScrollable.GetControlInDirection(NavigationDirection direction, Control? from) |
||||
|
{ |
||||
|
throw new NotImplementedException(); |
||||
|
} |
||||
|
|
||||
|
void ILogicalScrollable.RaiseScrollInvalidated(EventArgs e) => _scrollInvalidated?.Invoke(this, e); |
||||
|
|
||||
|
protected override Size MeasureOverride(Size availableSize) |
||||
|
{ |
||||
|
var items = ItemsControl?.Items as IList ?? Array.Empty<object?>(); |
||||
|
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<Control>? 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(IList items, NotifyCollectionChangedEventArgs e) |
||||
|
{ |
||||
|
base.OnItemsChanged(items, e); |
||||
|
|
||||
|
void Add(int index, int count) |
||||
|
{ |
||||
|
if (index <= _realizedIndex) |
||||
|
_realizedIndex += count; |
||||
|
} |
||||
|
|
||||
|
void Remove(int index, int count) |
||||
|
{ |
||||
|
var end = index + (count - 1); |
||||
|
|
||||
|
if (_realized is not null && index <= _realizedIndex && end >= _realizedIndex) |
||||
|
{ |
||||
|
RecycleElement(_realized); |
||||
|
_realized = null; |
||||
|
_realizedIndex = -1; |
||||
|
} |
||||
|
else if (index < _realizedIndex) |
||||
|
{ |
||||
|
_realizedIndex -= count; |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
switch (e.Action) |
||||
|
{ |
||||
|
case NotifyCollectionChangedAction.Add: |
||||
|
Add(e.NewStartingIndex, e.NewItems!.Count); |
||||
|
break; |
||||
|
case NotifyCollectionChangedAction.Remove: |
||||
|
Remove(e.OldStartingIndex, e.OldItems!.Count); |
||||
|
break; |
||||
|
case NotifyCollectionChangedAction.Replace: |
||||
|
case NotifyCollectionChangedAction.Move: |
||||
|
Remove(e.OldStartingIndex, e.OldItems!.Count); |
||||
|
Add(e.NewStartingIndex, e.NewItems!.Count); |
||||
|
break; |
||||
|
case NotifyCollectionChangedAction.Reset: |
||||
|
if (_realized is not null) |
||||
|
{ |
||||
|
RecycleElement(_realized); |
||||
|
_realized = null; |
||||
|
_realizedIndex = -1; |
||||
|
} |
||||
|
break; |
||||
|
} |
||||
|
|
||||
|
InvalidateMeasure(); |
||||
|
} |
||||
|
|
||||
|
private Control GetOrCreateElement(IList items, int index) |
||||
|
{ |
||||
|
return GetRealizedElement(index) ?? |
||||
|
GetItemIsOwnContainer(items, index) ?? |
||||
|
GetRecycledElement(items, index) ?? |
||||
|
CreateElement(items, index); |
||||
|
} |
||||
|
|
||||
|
private Control? GetRealizedElement(int index) |
||||
|
{ |
||||
|
return _realizedIndex == index ? _realized : null; |
||||
|
} |
||||
|
|
||||
|
private Control? GetItemIsOwnContainer(IList items, int index) |
||||
|
{ |
||||
|
Debug.Assert(ItemsControl is not null); |
||||
|
|
||||
|
if (items[index] is Control controlItem) |
||||
|
{ |
||||
|
var generator = ItemsControl!.ItemContainerGenerator; |
||||
|
|
||||
|
if (controlItem.IsSet(ItemIsOwnContainerProperty)) |
||||
|
{ |
||||
|
controlItem.IsVisible = true; |
||||
|
return controlItem; |
||||
|
} |
||||
|
else if (generator.IsItemItsOwnContainer(controlItem)) |
||||
|
{ |
||||
|
AddInternalChild(controlItem); |
||||
|
generator.PrepareItemContainer(controlItem, controlItem, index); |
||||
|
controlItem.SetValue(ItemIsOwnContainerProperty, true); |
||||
|
return controlItem; |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
return null; |
||||
|
} |
||||
|
|
||||
|
private Control? GetRecycledElement(IList items, int index) |
||||
|
{ |
||||
|
Debug.Assert(ItemsControl is not null); |
||||
|
|
||||
|
var generator = ItemsControl!.ItemContainerGenerator; |
||||
|
var item = items[index]; |
||||
|
|
||||
|
if (_recyclePool?.Count > 0) |
||||
|
{ |
||||
|
var recycled = _recyclePool.Pop(); |
||||
|
recycled.IsVisible = true; |
||||
|
generator.PrepareItemContainer(recycled, item, index); |
||||
|
return recycled; |
||||
|
} |
||||
|
|
||||
|
return null; |
||||
|
} |
||||
|
|
||||
|
private Control CreateElement(IList items, int index) |
||||
|
{ |
||||
|
Debug.Assert(ItemsControl is not null); |
||||
|
|
||||
|
var generator = ItemsControl!.ItemContainerGenerator; |
||||
|
var item = items[index]; |
||||
|
var container = generator.CreateContainer(); |
||||
|
|
||||
|
AddInternalChild(container); |
||||
|
generator.PrepareItemContainer(container, item, index); |
||||
|
|
||||
|
return container; |
||||
|
} |
||||
|
|
||||
|
private void RecycleElement(Control element) |
||||
|
{ |
||||
|
Debug.Assert(ItemsControl is not null); |
||||
|
|
||||
|
if (element.IsSet(ItemIsOwnContainerProperty)) |
||||
|
{ |
||||
|
element.IsVisible = false; |
||||
|
} |
||||
|
else |
||||
|
{ |
||||
|
ItemsControl!.ItemContainerGenerator.ClearItemContainer(element); |
||||
|
_recyclePool ??= new(); |
||||
|
_recyclePool.Push(element); |
||||
|
element.IsVisible = false; |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
private IPageTransition? GetTransition() => (ItemsControl as Carousel)?.PageTransition; |
||||
|
|
||||
|
private void TransitionFinished(Task task) |
||||
|
{ |
||||
|
if (task.IsCanceled) |
||||
|
return; |
||||
|
|
||||
|
if (_transitionFrom is not null) |
||||
|
RecycleElement(_transitionFrom); |
||||
|
_transition = null; |
||||
|
_transitionFrom = null; |
||||
|
_transitionFromIndex = -1; |
||||
|
} |
||||
|
} |
||||
|
} |
||||
@ -1,341 +1,321 @@ |
|||||
////using System.Collections.ObjectModel;
|
using System; |
||||
////using System.Linq;
|
using System.Collections.ObjectModel; |
||||
////using System.Reactive.Subjects;
|
using System.Linq; |
||||
////using Avalonia.Controls.Presenters;
|
using System.Reactive.Subjects; |
||||
////using Avalonia.Controls.Templates;
|
using Avalonia.Controls.Presenters; |
||||
////using Avalonia.Data;
|
using Avalonia.Controls.Primitives; |
||||
////using Avalonia.LogicalTree;
|
using Avalonia.Controls.Templates; |
||||
////using Avalonia.UnitTests;
|
using Avalonia.Data; |
||||
////using Avalonia.VisualTree;
|
using Avalonia.Layout; |
||||
////using Xunit;
|
using Avalonia.LogicalTree; |
||||
|
using Avalonia.UnitTests; |
||||
////namespace Avalonia.Controls.UnitTests
|
using Avalonia.VisualTree; |
||||
////{
|
using Xunit; |
||||
//// public class CarouselTests
|
|
||||
//// {
|
namespace Avalonia.Controls.UnitTests |
||||
//// [Fact]
|
{ |
||||
//// public void First_Item_Should_Be_Selected_By_Default()
|
public class CarouselTests |
||||
//// {
|
{ |
||||
//// var target = new Carousel
|
[Fact] |
||||
//// {
|
public void First_Item_Should_Be_Selected_By_Default() |
||||
//// Template = new FuncControlTemplate<Carousel>(CreateTemplate),
|
{ |
||||
//// Items = new[]
|
using var app = Start(); |
||||
//// {
|
var target = new Carousel |
||||
//// "Foo",
|
{ |
||||
//// "Bar"
|
Template = CarouselTemplate(), |
||||
//// }
|
Items = new[] |
||||
//// };
|
{ |
||||
|
"Foo", |
||||
//// target.ApplyTemplate();
|
"Bar" |
||||
|
} |
||||
//// Assert.Equal(0, target.SelectedIndex);
|
}; |
||||
//// Assert.Equal("Foo", target.SelectedItem);
|
|
||||
//// }
|
Prepare(target); |
||||
|
|
||||
//// [Fact]
|
Assert.Equal(0, target.SelectedIndex); |
||||
//// public void LogicalChild_Should_Be_Selected_Item()
|
Assert.Equal("Foo", target.SelectedItem); |
||||
//// {
|
} |
||||
//// var target = new Carousel
|
|
||||
//// {
|
[Fact] |
||||
//// Template = new FuncControlTemplate<Carousel>(CreateTemplate),
|
public void LogicalChild_Should_Be_Selected_Item() |
||||
//// Items = new[]
|
{ |
||||
//// {
|
using var app = Start(); |
||||
//// "Foo",
|
var target = new Carousel |
||||
//// "Bar"
|
{ |
||||
//// }
|
Template = CarouselTemplate(), |
||||
//// };
|
Items = new[] |
||||
|
{ |
||||
//// target.ApplyTemplate();
|
"Foo", |
||||
//// ((Control)target.Presenter).ApplyTemplate();
|
"Bar" |
||||
|
} |
||||
//// Assert.Single(target.GetLogicalChildren());
|
}; |
||||
|
|
||||
//// var child = GetContainerTextBlock(target.GetLogicalChildren().Single());
|
Prepare(target); |
||||
|
|
||||
//// Assert.Equal("Foo", child.Text);
|
Assert.Single(target.GetRealizedContainers()); |
||||
//// }
|
|
||||
|
var child = GetContainerTextBlock(target.GetRealizedContainers().Single()); |
||||
//// [Fact]
|
|
||||
//// public void Should_Remove_NonCurrent_Page_When_IsVirtualized_True()
|
Assert.Equal("Foo", child.Text); |
||||
//// {
|
} |
||||
//// var target = new Carousel
|
|
||||
//// {
|
[Fact] |
||||
//// Template = new FuncControlTemplate<Carousel>(CreateTemplate),
|
public void Selected_Item_Changes_To_First_Item_When_Items_Property_Changes() |
||||
//// Items = new[] { "foo", "bar" },
|
{ |
||||
//// IsVirtualized = true,
|
using var app = Start(); |
||||
//// SelectedIndex = 0,
|
var items = new ObservableCollection<string> |
||||
//// };
|
{ |
||||
|
"Foo", |
||||
//// target.ApplyTemplate();
|
"Bar", |
||||
//// ((Control)target.Presenter).ApplyTemplate();
|
"FooBar" |
||||
|
}; |
||||
//// Assert.Single(target.ItemContainerGenerator.Containers);
|
|
||||
//// target.SelectedIndex = 1;
|
var target = new Carousel |
||||
//// Assert.Single(target.ItemContainerGenerator.Containers);
|
{ |
||||
//// }
|
Template = CarouselTemplate(), |
||||
|
Items = items, |
||||
//// [Fact]
|
}; |
||||
//// public void Selected_Item_Changes_To_First_Item_When_Items_Property_Changes()
|
|
||||
//// {
|
Prepare(target); |
||||
//// var items = new ObservableCollection<string>
|
|
||||
//// {
|
Assert.Single(target.GetRealizedContainers()); |
||||
//// "Foo",
|
|
||||
//// "Bar",
|
var child = GetContainerTextBlock(target.GetRealizedContainers().Single()); |
||||
//// "FooBar"
|
|
||||
//// };
|
Assert.Equal("Foo", child.Text); |
||||
|
|
||||
//// var target = new Carousel
|
var newItems = items.ToList(); |
||||
//// {
|
newItems.RemoveAt(0); |
||||
//// Template = new FuncControlTemplate<Carousel>(CreateTemplate),
|
Layout(target); |
||||
//// Items = items,
|
|
||||
//// IsVirtualized = false
|
target.Items = newItems; |
||||
//// };
|
Layout(target); |
||||
|
|
||||
//// target.ApplyTemplate();
|
child = GetContainerTextBlock(target.GetRealizedContainers().Single()); |
||||
//// ((Control)target.Presenter).ApplyTemplate();
|
|
||||
|
Assert.Equal("Bar", child.Text); |
||||
//// Assert.Equal(3, target.GetLogicalChildren().Count());
|
} |
||||
|
|
||||
//// var child = GetContainerTextBlock(target.GetLogicalChildren().First());
|
[Fact] |
||||
|
public void Selected_Item_Changes_To_First_Item_When_Item_Added() |
||||
//// Assert.Equal("Foo", child.Text);
|
{ |
||||
|
using var app = Start(); |
||||
//// var newItems = items.ToList();
|
var items = new ObservableCollection<string>(); |
||||
//// newItems.RemoveAt(0);
|
var target = new Carousel |
||||
|
{ |
||||
//// target.Items = newItems;
|
Template = CarouselTemplate(), |
||||
|
Items = items, |
||||
//// child = GetContainerTextBlock(target.GetLogicalChildren().First());
|
}; |
||||
|
|
||||
//// Assert.Equal("Bar", child.Text);
|
Prepare(target); |
||||
//// }
|
|
||||
|
Assert.Equal(-1, target.SelectedIndex); |
||||
//// [Fact]
|
Assert.Empty(target.GetRealizedContainers()); |
||||
//// public void Selected_Item_Changes_To_First_Item_When_Items_Property_Changes_And_Virtualized()
|
|
||||
//// {
|
items.Add("Foo"); |
||||
//// var items = new ObservableCollection<string>
|
Layout(target); |
||||
//// {
|
|
||||
//// "Foo",
|
Assert.Equal(0, target.SelectedIndex); |
||||
//// "Bar",
|
Assert.Single(target.GetRealizedContainers()); |
||||
//// "FooBar"
|
} |
||||
//// };
|
|
||||
|
[Fact] |
||||
//// var target = new Carousel
|
public void Selected_Index_Changes_To_None_When_Items_Assigned_Null() |
||||
//// {
|
{ |
||||
//// Template = new FuncControlTemplate<Carousel>(CreateTemplate),
|
using var app = Start(); |
||||
//// Items = items,
|
var items = new ObservableCollection<string> |
||||
//// IsVirtualized = true,
|
{ |
||||
//// };
|
"Foo", |
||||
|
"Bar", |
||||
//// target.ApplyTemplate();
|
"FooBar" |
||||
//// ((Control)target.Presenter).ApplyTemplate();
|
}; |
||||
|
|
||||
//// Assert.Single(target.GetLogicalChildren());
|
var target = new Carousel |
||||
|
{ |
||||
//// var child = GetContainerTextBlock(target.GetLogicalChildren().Single());
|
Template = CarouselTemplate(), |
||||
|
Items = items, |
||||
//// Assert.Equal("Foo", child.Text);
|
}; |
||||
|
|
||||
//// var newItems = items.ToList();
|
Prepare(target); |
||||
//// newItems.RemoveAt(0);
|
|
||||
|
Assert.Equal(1, target.GetRealizedContainers().Count()); |
||||
//// target.Items = newItems;
|
|
||||
|
var child = GetContainerTextBlock(target.GetRealizedContainers().First()); |
||||
//// child = GetContainerTextBlock(target.GetLogicalChildren().Single());
|
|
||||
|
Assert.Equal("Foo", child.Text); |
||||
//// Assert.Equal("Bar", child.Text);
|
|
||||
//// }
|
target.Items = null; |
||||
|
Layout(target); |
||||
//// [Fact]
|
|
||||
//// public void Selected_Item_Changes_To_First_Item_When_Item_Added()
|
var numChildren = target.GetRealizedContainers().Count(); |
||||
//// {
|
|
||||
//// var items = new ObservableCollection<string>();
|
|
||||
//// var target = new Carousel
|
|
||||
//// {
|
|
||||
//// Template = new FuncControlTemplate<Carousel>(CreateTemplate),
|
|
||||
//// Items = items,
|
|
||||
//// IsVirtualized = false
|
|
||||
//// };
|
|
||||
|
|
||||
//// target.ApplyTemplate();
|
|
||||
//// ((Control)target.Presenter).ApplyTemplate();
|
|
||||
|
|
||||
//// Assert.Equal(-1, target.SelectedIndex);
|
Assert.Equal(-1, target.SelectedIndex); |
||||
//// Assert.Empty(target.GetLogicalChildren());
|
} |
||||
|
|
||||
//// items.Add("Foo");
|
[Fact] |
||||
|
public void Selected_Index_Is_Maintained_Carousel_Created_With_Non_Zero_SelectedIndex() |
||||
|
{ |
||||
|
using var app = Start(); |
||||
|
var items = new ObservableCollection<string> |
||||
|
{ |
||||
|
"Foo", |
||||
|
"Bar", |
||||
|
"FooBar" |
||||
|
}; |
||||
|
|
||||
|
var target = new Carousel |
||||
|
{ |
||||
|
Template = CarouselTemplate(), |
||||
|
Items = items, |
||||
|
SelectedIndex = 2 |
||||
|
}; |
||||
|
|
||||
//// Assert.Equal(0, target.SelectedIndex);
|
Prepare(target); |
||||
//// Assert.Single(target.GetLogicalChildren());
|
|
||||
//// }
|
Assert.Equal("FooBar", target.SelectedItem); |
||||
|
|
||||
//// [Fact]
|
var child = GetContainerTextBlock(target.GetRealizedContainers().LastOrDefault()); |
||||
//// public void Selected_Index_Changes_To_None_When_Items_Assigned_Null()
|
|
||||
//// {
|
|
||||
//// var items = new ObservableCollection<string>
|
|
||||
//// {
|
|
||||
//// "Foo",
|
|
||||
//// "Bar",
|
|
||||
//// "FooBar"
|
|
||||
//// };
|
|
||||
|
|
||||
//// var target = new Carousel
|
Assert.Equal("FooBar", child.Text); |
||||
//// {
|
} |
||||
//// Template = new FuncControlTemplate<Carousel>(CreateTemplate),
|
|
||||
//// Items = items,
|
[Fact] |
||||
//// IsVirtualized = false
|
public void Selected_Item_Changes_To_Next_First_Item_When_Item_Removed_From_Beggining_Of_List() |
||||
//// };
|
{ |
||||
|
using var app = Start(); |
||||
//// target.ApplyTemplate();
|
var items = new ObservableCollection<string> |
||||
//// ((Control)target.Presenter).ApplyTemplate();
|
{ |
||||
|
"Foo", |
||||
//// Assert.Equal(3, target.GetLogicalChildren().Count());
|
"Bar", |
||||
|
"FooBar" |
||||
//// var child = GetContainerTextBlock(target.GetLogicalChildren().First());
|
}; |
||||
|
|
||||
//// Assert.Equal("Foo", child.Text);
|
var target = new Carousel |
||||
|
{ |
||||
//// target.Items = null;
|
Template = CarouselTemplate(), |
||||
|
Items = items, |
||||
//// var numChildren = target.GetLogicalChildren().Count();
|
}; |
||||
|
|
||||
//// Assert.Equal(0, numChildren);
|
Prepare(target); |
||||
//// Assert.Equal(-1, target.SelectedIndex);
|
|
||||
//// }
|
var child = GetContainerTextBlock(target.GetRealizedContainers().First()); |
||||
|
|
||||
//// [Fact]
|
Assert.Equal("Foo", child.Text); |
||||
//// public void Selected_Index_Is_Maintained_Carousel_Created_With_Non_Zero_SelectedIndex()
|
|
||||
//// {
|
items.RemoveAt(0); |
||||
//// var items = new ObservableCollection<string>
|
Layout(target); |
||||
//// {
|
|
||||
//// "Foo",
|
child = GetContainerTextBlock(target.GetRealizedContainers().First()); |
||||
//// "Bar",
|
|
||||
//// "FooBar"
|
Assert.IsType<TextBlock>(child); |
||||
//// };
|
Assert.Equal("Bar", child.Text); |
||||
|
} |
||||
//// var target = new Carousel
|
|
||||
//// {
|
[Fact] |
||||
//// Template = new FuncControlTemplate<Carousel>(CreateTemplate),
|
public void Selected_Item_Changes_To_First_Item_If_SelectedItem_Is_Removed_From_Middle() |
||||
//// Items = items,
|
{ |
||||
//// IsVirtualized = false,
|
using var app = Start(); |
||||
//// SelectedIndex = 2
|
var items = new ObservableCollection<string> |
||||
//// };
|
{ |
||||
|
"Foo", |
||||
//// target.ApplyTemplate();
|
"Bar", |
||||
//// ((Control)target.Presenter).ApplyTemplate();
|
"FooBar" |
||||
|
}; |
||||
//// Assert.Equal("FooBar", target.SelectedItem);
|
|
||||
|
var target = new Carousel |
||||
//// var child = GetContainerTextBlock(target.GetVisualDescendants().LastOrDefault());
|
{ |
||||
|
Template = CarouselTemplate(), |
||||
//// Assert.IsType<TextBlock>(child);
|
Items = items, |
||||
//// Assert.Equal("FooBar", ((TextBlock)child).Text);
|
}; |
||||
//// }
|
|
||||
|
Prepare(target); |
||||
//// [Fact]
|
|
||||
//// public void Selected_Item_Changes_To_Next_First_Item_When_Item_Removed_From_Beggining_Of_List()
|
target.SelectedIndex = 1; |
||||
//// {
|
|
||||
//// var items = new ObservableCollection<string>
|
items.RemoveAt(1); |
||||
//// {
|
|
||||
//// "Foo",
|
Assert.Equal(0, target.SelectedIndex); |
||||
//// "Bar",
|
Assert.Equal("Foo", target.SelectedItem); |
||||
//// "FooBar"
|
} |
||||
//// };
|
|
||||
|
[Fact] |
||||
//// var target = new Carousel
|
public void SelectedItem_Validation() |
||||
//// {
|
{ |
||||
//// Template = new FuncControlTemplate<Carousel>(CreateTemplate),
|
using (UnitTestApplication.Start(TestServices.MockThreadingInterface)) |
||||
//// Items = items,
|
{ |
||||
//// IsVirtualized = false
|
var target = new Carousel |
||||
//// };
|
{ |
||||
|
Template = CarouselTemplate(), |
||||
//// target.ApplyTemplate();
|
}; |
||||
//// ((Control)target.Presenter).ApplyTemplate();
|
|
||||
|
Prepare(target); |
||||
//// Assert.Equal(3, target.GetLogicalChildren().Count());
|
|
||||
|
var exception = new System.InvalidCastException("failed validation"); |
||||
//// var child = GetContainerTextBlock(target.GetLogicalChildren().First());
|
var textObservable = |
||||
|
new BehaviorSubject<BindingNotification>(new BindingNotification(exception, |
||||
//// Assert.Equal("Foo", child.Text);
|
BindingErrorType.DataValidationError)); |
||||
|
target.Bind(ComboBox.SelectedItemProperty, textObservable); |
||||
//// items.RemoveAt(0);
|
|
||||
|
Assert.True(DataValidationErrors.GetHasErrors(target)); |
||||
//// child = GetContainerTextBlock(target.GetLogicalChildren().First());
|
Assert.True(DataValidationErrors.GetErrors(target).SequenceEqual(new[] { exception })); |
||||
|
} |
||||
//// Assert.IsType<TextBlock>(child);
|
} |
||||
//// Assert.Equal("Bar", ((TextBlock)child).Text);
|
|
||||
//// }
|
private static IDisposable Start() => UnitTestApplication.Start(TestServices.MockPlatformRenderInterface); |
||||
|
|
||||
//// [Fact]
|
private static void Prepare(Carousel target) |
||||
//// public void Selected_Item_Changes_To_First_Item_If_SelectedItem_Is_Removed_From_Middle()
|
{ |
||||
//// {
|
var root = new TestRoot(target); |
||||
//// var items = new ObservableCollection<string>
|
root.LayoutManager.ExecuteInitialLayoutPass(); |
||||
//// {
|
} |
||||
//// "Foo",
|
|
||||
//// "Bar",
|
private static void Layout(Carousel target) |
||||
//// "FooBar"
|
{ |
||||
//// };
|
((ILayoutRoot)target.GetVisualRoot()).LayoutManager.ExecuteLayoutPass(); |
||||
|
} |
||||
//// var target = new Carousel
|
|
||||
//// {
|
private static IControlTemplate CarouselTemplate() |
||||
//// Template = new FuncControlTemplate<Carousel>(CreateTemplate),
|
{ |
||||
//// Items = items,
|
return new FuncControlTemplate((c, ns) => |
||||
//// IsVirtualized = false
|
new ScrollViewer |
||||
//// };
|
{ |
||||
|
Name = "PART_ScrollViewer", |
||||
//// target.ApplyTemplate();
|
Template = ScrollViewerTemplate(), |
||||
//// ((Control)target.Presenter).ApplyTemplate();
|
HorizontalScrollBarVisibility = ScrollBarVisibility.Hidden, |
||||
|
VerticalScrollBarVisibility = ScrollBarVisibility.Hidden, |
||||
//// target.SelectedIndex = 1;
|
Content = new ItemsPresenter |
||||
|
{ |
||||
//// items.RemoveAt(1);
|
Name = "PART_ItemsPresenter", |
||||
|
[~ItemsPresenter.ItemsPanelProperty] = c[~ItemsControl.ItemsPanelProperty], |
||||
//// Assert.Equal(0, target.SelectedIndex);
|
}.RegisterInNameScope(ns) |
||||
//// Assert.Equal("Foo", target.SelectedItem);
|
}.RegisterInNameScope(ns)); |
||||
//// }
|
} |
||||
|
|
||||
//// private Control CreateTemplate(Carousel control, INameScope scope)
|
private static FuncControlTemplate ScrollViewerTemplate() |
||||
//// {
|
{ |
||||
//// return new CarouselPresenter
|
return new FuncControlTemplate<ScrollViewer>((parent, scope) => |
||||
//// {
|
new Panel |
||||
//// Name = "PART_ItemsPresenter",
|
{ |
||||
//// [~CarouselPresenter.IsVirtualizedProperty] = control[~Carousel.IsVirtualizedProperty],
|
Children = |
||||
//// [~CarouselPresenter.ItemsPanelProperty] = control[~Carousel.ItemsPanelProperty],
|
{ |
||||
//// [~CarouselPresenter.SelectedIndexProperty] = control[~Carousel.SelectedIndexProperty],
|
new ScrollContentPresenter |
||||
//// [~CarouselPresenter.PageTransitionProperty] = control[~Carousel.PageTransitionProperty],
|
{ |
||||
//// }.RegisterInNameScope(scope);
|
Name = "PART_ContentPresenter", |
||||
//// }
|
[~ScrollContentPresenter.ContentProperty] = parent.GetObservable(ScrollViewer.ContentProperty).ToBinding(), |
||||
|
[~~ScrollContentPresenter.ExtentProperty] = parent[~~ScrollViewer.ExtentProperty], |
||||
//// private static TextBlock GetContainerTextBlock(object control)
|
[~~ScrollContentPresenter.OffsetProperty] = parent[~~ScrollViewer.OffsetProperty], |
||||
//// {
|
[~~ScrollContentPresenter.ViewportProperty] = parent[~~ScrollViewer.ViewportProperty], |
||||
//// var contentPresenter = Assert.IsType<ContentPresenter>(control);
|
[~ScrollContentPresenter.CanHorizontallyScrollProperty] = parent[~ScrollViewer.CanHorizontallyScrollProperty], |
||||
//// contentPresenter.UpdateChild();
|
[~ScrollContentPresenter.CanVerticallyScrollProperty] = parent[~ScrollViewer.CanVerticallyScrollProperty], |
||||
//// return Assert.IsType<TextBlock>(contentPresenter.Child);
|
}.RegisterInNameScope(scope), |
||||
//// }
|
} |
||||
|
}); |
||||
//// [Fact]
|
} |
||||
//// public void SelectedItem_Validation()
|
|
||||
//// {
|
private static TextBlock GetContainerTextBlock(object control) |
||||
//// using (UnitTestApplication.Start(TestServices.MockThreadingInterface))
|
{ |
||||
//// {
|
var contentPresenter = Assert.IsType<ContentPresenter>(control); |
||||
//// var target = new Carousel
|
return Assert.IsType<TextBlock>(contentPresenter.Child); |
||||
//// {
|
} |
||||
//// Template = new FuncControlTemplate<Carousel>(CreateTemplate),
|
} |
||||
//// IsVirtualized = false
|
} |
||||
//// };
|
|
||||
|
|
||||
//// target.ApplyTemplate();
|
|
||||
//// ((Control)target.Presenter).ApplyTemplate();
|
|
||||
|
|
||||
//// var exception = new System.InvalidCastException("failed validation");
|
|
||||
//// var textObservable =
|
|
||||
//// new BehaviorSubject<BindingNotification>(new BindingNotification(exception,
|
|
||||
//// BindingErrorType.DataValidationError));
|
|
||||
//// target.Bind(ComboBox.SelectedItemProperty, textObservable);
|
|
||||
|
|
||||
//// Assert.True(DataValidationErrors.GetHasErrors(target));
|
|
||||
//// Assert.True(DataValidationErrors.GetErrors(target).SequenceEqual(new[] { exception }));
|
|
||||
//// }
|
|
||||
//// }
|
|
||||
//// }
|
|
||||
////}
|
|
||||
|
|||||
@ -1,732 +0,0 @@ |
|||||
////using System.Linq;
|
|
||||
////using Moq;
|
|
||||
////using Avalonia.Controls.Generators;
|
|
||||
////using Avalonia.Controls.Presenters;
|
|
||||
////using Avalonia.Controls.Templates;
|
|
||||
////using Xunit;
|
|
||||
////using System.Collections.ObjectModel;
|
|
||||
////using System.Collections;
|
|
||||
|
|
||||
////namespace Avalonia.Controls.UnitTests.Presenters
|
|
||||
////{
|
|
||||
//// public class CarouselPresenterTests
|
|
||||
//// {
|
|
||||
//// [Fact]
|
|
||||
//// public void Should_Register_With_Host_When_TemplatedParent_Set()
|
|
||||
//// {
|
|
||||
//// var host = new Carousel();
|
|
||||
//// var target = new CarouselPresenter();
|
|
||||
|
|
||||
//// Assert.Null(host.Presenter);
|
|
||||
|
|
||||
//// target.SetValue(Control.TemplatedParentProperty, host);
|
|
||||
|
|
||||
//// Assert.Same(target, host.Presenter);
|
|
||||
//// }
|
|
||||
|
|
||||
//// [Fact]
|
|
||||
//// public void ApplyTemplate_Should_Create_Panel()
|
|
||||
//// {
|
|
||||
//// var target = new CarouselPresenter
|
|
||||
//// {
|
|
||||
//// ItemsPanel = new FuncTemplate<Panel>(() => new Panel()),
|
|
||||
//// };
|
|
||||
|
|
||||
//// target.ApplyTemplate();
|
|
||||
|
|
||||
//// Assert.IsType<Panel>(target.Panel);
|
|
||||
//// }
|
|
||||
|
|
||||
//// [Fact]
|
|
||||
//// public void ItemContainerGenerator_Should_Be_Picked_Up_From_TemplatedControl()
|
|
||||
//// {
|
|
||||
//// var parent = new TestItemsControl();
|
|
||||
//// var target = new CarouselPresenter
|
|
||||
//// {
|
|
||||
//// [StyledElement.TemplatedParentProperty] = parent,
|
|
||||
//// };
|
|
||||
|
|
||||
//// Assert.IsType<ItemContainerGenerator<TestItem>>(target.ItemContainerGenerator);
|
|
||||
//// }
|
|
||||
|
|
||||
//// public class Virtualized
|
|
||||
//// {
|
|
||||
//// [Fact]
|
|
||||
//// public void Should_Initially_Materialize_Selected_Container()
|
|
||||
//// {
|
|
||||
//// var target = new CarouselPresenter
|
|
||||
//// {
|
|
||||
//// Items = new[] { "foo", "bar" },
|
|
||||
//// SelectedIndex = 0,
|
|
||||
//// IsVirtualized = true,
|
|
||||
//// };
|
|
||||
|
|
||||
//// target.ApplyTemplate();
|
|
||||
|
|
||||
//// AssertSingle(target);
|
|
||||
//// }
|
|
||||
|
|
||||
//// [Fact]
|
|
||||
//// public void Should_Initially_Materialize_Nothing_If_No_Selected_Container()
|
|
||||
//// {
|
|
||||
//// var target = new CarouselPresenter
|
|
||||
//// {
|
|
||||
//// Items = new[] { "foo", "bar" },
|
|
||||
//// IsVirtualized = true,
|
|
||||
//// };
|
|
||||
|
|
||||
//// target.ApplyTemplate();
|
|
||||
|
|
||||
//// Assert.Empty(target.Panel.Children);
|
|
||||
//// Assert.Empty(target.ItemContainerGenerator.Containers);
|
|
||||
//// }
|
|
||||
|
|
||||
//// [Fact]
|
|
||||
//// public void Switching_To_Virtualized_Should_Reset_Containers()
|
|
||||
//// {
|
|
||||
//// var target = new CarouselPresenter
|
|
||||
//// {
|
|
||||
//// Items = new[] { "foo", "bar" },
|
|
||||
//// SelectedIndex = 0,
|
|
||||
//// IsVirtualized = false,
|
|
||||
//// };
|
|
||||
|
|
||||
//// target.ApplyTemplate();
|
|
||||
//// target.IsVirtualized = true;
|
|
||||
|
|
||||
//// AssertSingle(target);
|
|
||||
//// }
|
|
||||
|
|
||||
//// [Fact]
|
|
||||
//// public void Changing_SelectedIndex_Should_Show_Page()
|
|
||||
//// {
|
|
||||
//// var target = new CarouselPresenter
|
|
||||
//// {
|
|
||||
//// Items = new[] { "foo", "bar" },
|
|
||||
//// SelectedIndex = 0,
|
|
||||
//// IsVirtualized = true,
|
|
||||
//// };
|
|
||||
|
|
||||
//// target.ApplyTemplate();
|
|
||||
//// AssertSingle(target);
|
|
||||
|
|
||||
//// target.SelectedIndex = 1;
|
|
||||
//// AssertSingle(target);
|
|
||||
//// }
|
|
||||
|
|
||||
//// [Fact]
|
|
||||
//// public void Should_Remove_NonCurrent_Page()
|
|
||||
//// {
|
|
||||
//// var target = new CarouselPresenter
|
|
||||
//// {
|
|
||||
//// Items = new[] { "foo", "bar" },
|
|
||||
//// IsVirtualized = true,
|
|
||||
//// SelectedIndex = 0,
|
|
||||
//// };
|
|
||||
|
|
||||
//// target.ApplyTemplate();
|
|
||||
//// AssertSingle(target);
|
|
||||
|
|
||||
//// target.SelectedIndex = 1;
|
|
||||
//// AssertSingle(target);
|
|
||||
|
|
||||
//// }
|
|
||||
|
|
||||
//// [Fact]
|
|
||||
//// public void Should_Handle_Inserting_Item_At_SelectedItem()
|
|
||||
//// {
|
|
||||
//// var items = new ObservableCollection<string>
|
|
||||
//// {
|
|
||||
//// "item0",
|
|
||||
//// "item1",
|
|
||||
//// "item2",
|
|
||||
//// };
|
|
||||
|
|
||||
//// var target = new CarouselPresenter
|
|
||||
//// {
|
|
||||
//// Items = items,
|
|
||||
//// SelectedIndex = 1,
|
|
||||
//// IsVirtualized = true,
|
|
||||
//// };
|
|
||||
|
|
||||
//// target.ApplyTemplate();
|
|
||||
|
|
||||
//// items.Insert(1, "item1a");
|
|
||||
//// AssertSingle(target);
|
|
||||
//// }
|
|
||||
|
|
||||
//// [Fact]
|
|
||||
//// public void Should_Handle_Inserting_Item_Before_SelectedItem()
|
|
||||
//// {
|
|
||||
//// var items = new ObservableCollection<string>
|
|
||||
//// {
|
|
||||
//// "item0",
|
|
||||
//// "item1",
|
|
||||
//// "item2",
|
|
||||
//// };
|
|
||||
|
|
||||
//// var target = new CarouselPresenter
|
|
||||
//// {
|
|
||||
//// Items = items,
|
|
||||
//// SelectedIndex = 2,
|
|
||||
//// IsVirtualized = true,
|
|
||||
//// };
|
|
||||
|
|
||||
//// target.ApplyTemplate();
|
|
||||
|
|
||||
//// items.Insert(1, "item1a");
|
|
||||
//// AssertSingle(target);
|
|
||||
//// }
|
|
||||
|
|
||||
//// [Fact]
|
|
||||
//// public void Should_Do_Nothing_When_Inserting_Item_After_SelectedItem()
|
|
||||
//// {
|
|
||||
//// var items = new ObservableCollection<string>
|
|
||||
//// {
|
|
||||
//// "item0",
|
|
||||
//// "item1",
|
|
||||
//// "item2",
|
|
||||
//// };
|
|
||||
|
|
||||
//// var target = new CarouselPresenter
|
|
||||
//// {
|
|
||||
//// Items = items,
|
|
||||
//// SelectedIndex = 1,
|
|
||||
//// IsVirtualized = true,
|
|
||||
//// };
|
|
||||
|
|
||||
//// target.ApplyTemplate();
|
|
||||
//// var child = AssertSingle(target);
|
|
||||
//// items.Insert(2, "after");
|
|
||||
//// Assert.Same(child, AssertSingle(target));
|
|
||||
//// }
|
|
||||
|
|
||||
//// [Fact]
|
|
||||
//// public void Should_Handle_Removing_Item_At_SelectedItem()
|
|
||||
//// {
|
|
||||
//// var items = new ObservableCollection<string>
|
|
||||
//// {
|
|
||||
//// "item0",
|
|
||||
//// "item1",
|
|
||||
//// "item2",
|
|
||||
//// };
|
|
||||
|
|
||||
//// var target = new CarouselPresenter
|
|
||||
//// {
|
|
||||
//// Items = items,
|
|
||||
//// SelectedIndex = 1,
|
|
||||
//// IsVirtualized = true,
|
|
||||
//// };
|
|
||||
|
|
||||
//// target.ApplyTemplate();
|
|
||||
|
|
||||
//// items.RemoveAt(1);
|
|
||||
//// AssertSingle(target);
|
|
||||
//// }
|
|
||||
|
|
||||
//// [Fact]
|
|
||||
//// public void Should_Handle_Removing_Item_Before_SelectedItem()
|
|
||||
//// {
|
|
||||
//// var items = new ObservableCollection<string>
|
|
||||
//// {
|
|
||||
//// "item0",
|
|
||||
//// "item1",
|
|
||||
//// "item2",
|
|
||||
//// };
|
|
||||
|
|
||||
//// var target = new CarouselPresenter
|
|
||||
//// {
|
|
||||
//// Items = items,
|
|
||||
//// SelectedIndex = 1,
|
|
||||
//// IsVirtualized = true,
|
|
||||
//// };
|
|
||||
|
|
||||
//// target.ApplyTemplate();
|
|
||||
|
|
||||
//// items.RemoveAt(0);
|
|
||||
//// AssertSingle(target);
|
|
||||
//// }
|
|
||||
|
|
||||
//// [Fact]
|
|
||||
//// public void Should_Do_Nothing_When_Removing_Item_After_SelectedItem()
|
|
||||
//// {
|
|
||||
//// var items = new ObservableCollection<string>
|
|
||||
//// {
|
|
||||
//// "item0",
|
|
||||
//// "item1",
|
|
||||
//// "item2",
|
|
||||
//// };
|
|
||||
|
|
||||
//// var target = new CarouselPresenter
|
|
||||
//// {
|
|
||||
//// Items = items,
|
|
||||
//// SelectedIndex = 1,
|
|
||||
//// IsVirtualized = true,
|
|
||||
//// };
|
|
||||
|
|
||||
//// target.ApplyTemplate();
|
|
||||
//// var child = AssertSingle(target);
|
|
||||
//// items.RemoveAt(2);
|
|
||||
//// Assert.Same(child, AssertSingle(target));
|
|
||||
//// }
|
|
||||
|
|
||||
//// [Fact]
|
|
||||
//// public void Should_Handle_Removing_SelectedItem_When_Its_Last()
|
|
||||
//// {
|
|
||||
//// var items = new ObservableCollection<string>
|
|
||||
//// {
|
|
||||
//// "item0",
|
|
||||
//// "item1",
|
|
||||
//// "item2",
|
|
||||
//// };
|
|
||||
|
|
||||
//// var target = new CarouselPresenter
|
|
||||
//// {
|
|
||||
//// Items = items,
|
|
||||
//// SelectedIndex = 2,
|
|
||||
//// IsVirtualized = true,
|
|
||||
//// };
|
|
||||
|
|
||||
//// target.ApplyTemplate();
|
|
||||
|
|
||||
//// items.RemoveAt(2);
|
|
||||
//// Assert.Equal(1, target.SelectedIndex);
|
|
||||
//// AssertSingle(target);
|
|
||||
//// }
|
|
||||
|
|
||||
//// [Fact]
|
|
||||
//// public void Should_Handle_Removing_Last_Item()
|
|
||||
//// {
|
|
||||
//// var items = new ObservableCollection<string>
|
|
||||
//// {
|
|
||||
//// "item0",
|
|
||||
//// };
|
|
||||
|
|
||||
//// var target = new CarouselPresenter
|
|
||||
//// {
|
|
||||
//// Items = items,
|
|
||||
//// SelectedIndex = 0,
|
|
||||
//// IsVirtualized = true,
|
|
||||
//// };
|
|
||||
|
|
||||
//// target.ApplyTemplate();
|
|
||||
|
|
||||
//// items.RemoveAt(0);
|
|
||||
//// Assert.Empty(target.Panel.Children);
|
|
||||
//// Assert.Empty(target.ItemContainerGenerator.Containers);
|
|
||||
//// }
|
|
||||
|
|
||||
//// [Fact]
|
|
||||
//// public void Should_Handle_Replacing_SelectedItem()
|
|
||||
//// {
|
|
||||
//// var items = new ObservableCollection<string>
|
|
||||
//// {
|
|
||||
//// "item0",
|
|
||||
//// "item1",
|
|
||||
//// "item2",
|
|
||||
//// };
|
|
||||
|
|
||||
//// var target = new CarouselPresenter
|
|
||||
//// {
|
|
||||
//// Items = items,
|
|
||||
//// SelectedIndex = 1,
|
|
||||
//// IsVirtualized = true,
|
|
||||
//// };
|
|
||||
|
|
||||
//// target.ApplyTemplate();
|
|
||||
|
|
||||
//// items[1] = "replaced";
|
|
||||
//// AssertSingle(target);
|
|
||||
//// }
|
|
||||
|
|
||||
//// [Fact]
|
|
||||
//// public void Should_Do_Nothing_When_Replacing_Non_SelectedItem()
|
|
||||
//// {
|
|
||||
//// var items = new ObservableCollection<string>
|
|
||||
//// {
|
|
||||
//// "item0",
|
|
||||
//// "item1",
|
|
||||
//// "item2",
|
|
||||
//// };
|
|
||||
|
|
||||
//// var target = new CarouselPresenter
|
|
||||
//// {
|
|
||||
//// Items = items,
|
|
||||
//// SelectedIndex = 1,
|
|
||||
//// IsVirtualized = true,
|
|
||||
//// };
|
|
||||
|
|
||||
//// target.ApplyTemplate();
|
|
||||
//// var child = AssertSingle(target);
|
|
||||
//// items[0] = "replaced";
|
|
||||
//// Assert.Same(child, AssertSingle(target));
|
|
||||
//// }
|
|
||||
|
|
||||
//// [Fact]
|
|
||||
//// public void Should_Handle_Moving_SelectedItem()
|
|
||||
//// {
|
|
||||
//// var items = new ObservableCollection<string>
|
|
||||
//// {
|
|
||||
//// "item0",
|
|
||||
//// "item1",
|
|
||||
//// "item2",
|
|
||||
//// };
|
|
||||
|
|
||||
//// var target = new CarouselPresenter
|
|
||||
//// {
|
|
||||
//// Items = items,
|
|
||||
//// SelectedIndex = 1,
|
|
||||
//// IsVirtualized = true,
|
|
||||
//// };
|
|
||||
|
|
||||
//// target.ApplyTemplate();
|
|
||||
|
|
||||
//// items.Move(1, 0);
|
|
||||
//// AssertSingle(target);
|
|
||||
//// }
|
|
||||
|
|
||||
//// private static Control AssertSingle(CarouselPresenter target)
|
|
||||
//// {
|
|
||||
//// var items = (IList)target.Items;
|
|
||||
//// var index = target.SelectedIndex;
|
|
||||
//// var content = items[index];
|
|
||||
//// var child = Assert.Single(target.Panel.Children);
|
|
||||
//// var presenter = Assert.IsType<ContentPresenter>(child);
|
|
||||
//// var container = Assert.Single(target.ItemContainerGenerator.Containers);
|
|
||||
//// var visible = Assert.Single(target.Panel.Children.Where(x => x.IsVisible));
|
|
||||
|
|
||||
//// Assert.Same(child, container.ContainerControl);
|
|
||||
//// Assert.Same(child, visible);
|
|
||||
//// Assert.Equal(content, presenter.Content);
|
|
||||
//// Assert.Equal(content, container.Item);
|
|
||||
//// Assert.Equal(index, container.Index);
|
|
||||
|
|
||||
//// return child;
|
|
||||
//// }
|
|
||||
//// }
|
|
||||
|
|
||||
//// public class NonVirtualized
|
|
||||
//// {
|
|
||||
//// [Fact]
|
|
||||
//// public void Should_Initially_Materialize_All_Containers()
|
|
||||
//// {
|
|
||||
//// var target = new CarouselPresenter
|
|
||||
//// {
|
|
||||
//// Items = new[] { "foo", "bar" },
|
|
||||
//// IsVirtualized = false,
|
|
||||
//// };
|
|
||||
|
|
||||
//// target.ApplyTemplate();
|
|
||||
//// AssertAll(target);
|
|
||||
//// }
|
|
||||
|
|
||||
//// [Fact]
|
|
||||
//// public void Should_Initially_Show_Selected_Item()
|
|
||||
//// {
|
|
||||
//// var target = new CarouselPresenter
|
|
||||
//// {
|
|
||||
//// Items = new[] { "foo", "bar" },
|
|
||||
//// SelectedIndex = 1,
|
|
||||
//// IsVirtualized = false,
|
|
||||
//// };
|
|
||||
|
|
||||
//// target.ApplyTemplate();
|
|
||||
//// AssertAll(target);
|
|
||||
//// }
|
|
||||
|
|
||||
//// [Fact]
|
|
||||
//// public void Switching_To_Non_Virtualized_Should_Reset_Containers()
|
|
||||
//// {
|
|
||||
//// var target = new CarouselPresenter
|
|
||||
//// {
|
|
||||
//// Items = new[] { "foo", "bar" },
|
|
||||
//// SelectedIndex = 0,
|
|
||||
//// IsVirtualized = true,
|
|
||||
//// };
|
|
||||
|
|
||||
//// target.ApplyTemplate();
|
|
||||
//// target.IsVirtualized = false;
|
|
||||
|
|
||||
//// AssertAll(target);
|
|
||||
//// }
|
|
||||
|
|
||||
//// [Fact]
|
|
||||
//// public void Changing_SelectedIndex_Should_Show_Page()
|
|
||||
//// {
|
|
||||
//// var target = new CarouselPresenter
|
|
||||
//// {
|
|
||||
//// Items = new[] { "foo", "bar" },
|
|
||||
//// SelectedIndex = 0,
|
|
||||
//// IsVirtualized = false,
|
|
||||
//// };
|
|
||||
|
|
||||
//// target.ApplyTemplate();
|
|
||||
//// AssertAll(target);
|
|
||||
|
|
||||
//// target.SelectedIndex = 1;
|
|
||||
//// AssertAll(target);
|
|
||||
//// }
|
|
||||
|
|
||||
//// [Fact]
|
|
||||
//// public void Should_Handle_Inserting_Item_At_SelectedItem()
|
|
||||
//// {
|
|
||||
//// var items = new ObservableCollection<string>
|
|
||||
//// {
|
|
||||
//// "item0",
|
|
||||
//// "item1",
|
|
||||
//// "item2",
|
|
||||
//// };
|
|
||||
|
|
||||
//// var target = new CarouselPresenter
|
|
||||
//// {
|
|
||||
//// Items = items,
|
|
||||
//// SelectedIndex = 1,
|
|
||||
//// IsVirtualized = false,
|
|
||||
//// };
|
|
||||
|
|
||||
//// target.ApplyTemplate();
|
|
||||
|
|
||||
//// items.Insert(1, "item1a");
|
|
||||
//// AssertAll(target);
|
|
||||
//// }
|
|
||||
|
|
||||
//// [Fact]
|
|
||||
//// public void Should_Handle_Inserting_Item_Before_SelectedItem()
|
|
||||
//// {
|
|
||||
//// var items = new ObservableCollection<string>
|
|
||||
//// {
|
|
||||
//// "item0",
|
|
||||
//// "item1",
|
|
||||
//// "item2",
|
|
||||
//// };
|
|
||||
|
|
||||
//// var target = new CarouselPresenter
|
|
||||
//// {
|
|
||||
//// Items = items,
|
|
||||
//// SelectedIndex = 2,
|
|
||||
//// IsVirtualized = false,
|
|
||||
//// };
|
|
||||
|
|
||||
//// target.ApplyTemplate();
|
|
||||
|
|
||||
//// items.Insert(1, "item1a");
|
|
||||
//// AssertAll(target);
|
|
||||
//// }
|
|
||||
|
|
||||
//// [Fact]
|
|
||||
//// public void Should_Do_Handle_Inserting_Item_After_SelectedItem()
|
|
||||
//// {
|
|
||||
//// var items = new ObservableCollection<string>
|
|
||||
//// {
|
|
||||
//// "item0",
|
|
||||
//// "item1",
|
|
||||
//// "item2",
|
|
||||
//// };
|
|
||||
|
|
||||
//// var target = new CarouselPresenter
|
|
||||
//// {
|
|
||||
//// Items = items,
|
|
||||
//// SelectedIndex = 1,
|
|
||||
//// IsVirtualized = false,
|
|
||||
//// };
|
|
||||
|
|
||||
//// target.ApplyTemplate();
|
|
||||
//// items.Insert(2, "after");
|
|
||||
//// AssertAll(target);
|
|
||||
//// }
|
|
||||
|
|
||||
//// [Fact]
|
|
||||
//// public void Should_Handle_Removing_Item_At_SelectedItem()
|
|
||||
//// {
|
|
||||
//// var items = new ObservableCollection<string>
|
|
||||
//// {
|
|
||||
//// "item0",
|
|
||||
//// "item1",
|
|
||||
//// "item2",
|
|
||||
//// };
|
|
||||
|
|
||||
//// var target = new CarouselPresenter
|
|
||||
//// {
|
|
||||
//// Items = items,
|
|
||||
//// SelectedIndex = 1,
|
|
||||
//// IsVirtualized = false,
|
|
||||
//// };
|
|
||||
|
|
||||
//// target.ApplyTemplate();
|
|
||||
|
|
||||
//// items.RemoveAt(1);
|
|
||||
//// AssertAll(target);
|
|
||||
//// }
|
|
||||
|
|
||||
//// [Fact]
|
|
||||
//// public void Should_Handle_Removing_Item_Before_SelectedItem()
|
|
||||
//// {
|
|
||||
//// var items = new ObservableCollection<string>
|
|
||||
//// {
|
|
||||
//// "item0",
|
|
||||
//// "item1",
|
|
||||
//// "item2",
|
|
||||
//// };
|
|
||||
|
|
||||
//// var target = new CarouselPresenter
|
|
||||
//// {
|
|
||||
//// Items = items,
|
|
||||
//// SelectedIndex = 1,
|
|
||||
//// IsVirtualized = false,
|
|
||||
//// };
|
|
||||
|
|
||||
//// target.ApplyTemplate();
|
|
||||
|
|
||||
//// items.RemoveAt(0);
|
|
||||
//// AssertAll(target);
|
|
||||
//// }
|
|
||||
|
|
||||
//// [Fact]
|
|
||||
//// public void Should_Handle_Removing_Item_After_SelectedItem()
|
|
||||
//// {
|
|
||||
//// var items = new ObservableCollection<string>
|
|
||||
//// {
|
|
||||
//// "item0",
|
|
||||
//// "item1",
|
|
||||
//// "item2",
|
|
||||
//// };
|
|
||||
|
|
||||
//// var target = new CarouselPresenter
|
|
||||
//// {
|
|
||||
//// Items = items,
|
|
||||
//// SelectedIndex = 1,
|
|
||||
//// IsVirtualized = false,
|
|
||||
//// };
|
|
||||
|
|
||||
//// target.ApplyTemplate();
|
|
||||
//// items.RemoveAt(2);
|
|
||||
//// AssertAll(target);
|
|
||||
//// }
|
|
||||
|
|
||||
//// [Fact]
|
|
||||
//// public void Should_Handle_Removing_SelectedItem_When_Its_Last()
|
|
||||
//// {
|
|
||||
//// var items = new ObservableCollection<string>
|
|
||||
//// {
|
|
||||
//// "item0",
|
|
||||
//// "item1",
|
|
||||
//// "item2",
|
|
||||
//// };
|
|
||||
|
|
||||
//// var target = new CarouselPresenter
|
|
||||
//// {
|
|
||||
//// Items = items,
|
|
||||
//// SelectedIndex = 2,
|
|
||||
//// IsVirtualized = false,
|
|
||||
//// };
|
|
||||
|
|
||||
//// target.ApplyTemplate();
|
|
||||
|
|
||||
//// items.RemoveAt(2);
|
|
||||
//// Assert.Equal(1, target.SelectedIndex);
|
|
||||
//// AssertAll(target);
|
|
||||
//// }
|
|
||||
|
|
||||
//// [Fact]
|
|
||||
//// public void Should_Handle_Removing_Last_Item()
|
|
||||
//// {
|
|
||||
//// var items = new ObservableCollection<string>
|
|
||||
//// {
|
|
||||
//// "item0",
|
|
||||
//// };
|
|
||||
|
|
||||
//// var target = new CarouselPresenter
|
|
||||
//// {
|
|
||||
//// Items = items,
|
|
||||
//// SelectedIndex = 0,
|
|
||||
//// IsVirtualized = false,
|
|
||||
//// };
|
|
||||
|
|
||||
//// target.ApplyTemplate();
|
|
||||
|
|
||||
//// items.RemoveAt(0);
|
|
||||
//// Assert.Empty(target.Panel.Children);
|
|
||||
//// Assert.Empty(target.ItemContainerGenerator.Containers);
|
|
||||
//// }
|
|
||||
|
|
||||
//// [Fact]
|
|
||||
//// public void Should_Handle_Replacing_SelectedItem()
|
|
||||
//// {
|
|
||||
//// var items = new ObservableCollection<string>
|
|
||||
//// {
|
|
||||
//// "item0",
|
|
||||
//// "item1",
|
|
||||
//// "item2",
|
|
||||
//// };
|
|
||||
|
|
||||
//// var target = new CarouselPresenter
|
|
||||
//// {
|
|
||||
//// Items = items,
|
|
||||
//// SelectedIndex = 1,
|
|
||||
//// IsVirtualized = false,
|
|
||||
//// };
|
|
||||
|
|
||||
//// target.ApplyTemplate();
|
|
||||
|
|
||||
//// items[1] = "replaced";
|
|
||||
//// AssertAll(target);
|
|
||||
//// }
|
|
||||
|
|
||||
//// [Fact]
|
|
||||
//// public void Should_Handle_Moving_SelectedItem()
|
|
||||
//// {
|
|
||||
//// var items = new ObservableCollection<string>
|
|
||||
//// {
|
|
||||
//// "item0",
|
|
||||
//// "item1",
|
|
||||
//// "item2",
|
|
||||
//// };
|
|
||||
|
|
||||
//// var target = new CarouselPresenter
|
|
||||
//// {
|
|
||||
//// Items = items,
|
|
||||
//// SelectedIndex = 1,
|
|
||||
//// IsVirtualized = false,
|
|
||||
//// };
|
|
||||
|
|
||||
//// target.ApplyTemplate();
|
|
||||
|
|
||||
//// items.Move(1, 0);
|
|
||||
//// AssertAll(target);
|
|
||||
//// }
|
|
||||
|
|
||||
//// private static void AssertAll(CarouselPresenter target)
|
|
||||
//// {
|
|
||||
//// var items = (IList)target.Items;
|
|
||||
|
|
||||
//// Assert.Equal(items?.Count ?? 0, target.Panel.Children.Count);
|
|
||||
//// Assert.Equal(items?.Count ?? 0, target.ItemContainerGenerator.Containers.Count());
|
|
||||
|
|
||||
//// for (var i = 0; i < items?.Count; ++i)
|
|
||||
//// {
|
|
||||
//// var content = items[i];
|
|
||||
//// var child = target.Panel.Children[i];
|
|
||||
//// var presenter = Assert.IsType<ContentPresenter>(child);
|
|
||||
//// var container = target.ItemContainerGenerator.ContainerFromIndex(i);
|
|
||||
|
|
||||
//// Assert.Same(child, container);
|
|
||||
//// Assert.Equal(i == target.SelectedIndex, child.IsVisible);
|
|
||||
//// Assert.Equal(content, presenter.Content);
|
|
||||
//// Assert.Equal(i, target.ItemContainerGenerator.IndexFromContainer(container));
|
|
||||
//// }
|
|
||||
//// }
|
|
||||
//// }
|
|
||||
|
|
||||
//// private class TestItem : ContentControl
|
|
||||
//// {
|
|
||||
//// }
|
|
||||
|
|
||||
//// private class TestItemsControl : ItemsControl
|
|
||||
//// {
|
|
||||
//// protected override IItemContainerGenerator CreateItemContainerGenerator()
|
|
||||
//// {
|
|
||||
//// return new ItemContainerGenerator<TestItem>(this, TestItem.ContentProperty, null);
|
|
||||
//// }
|
|
||||
//// }
|
|
||||
//// }
|
|
||||
////}
|
|
||||
@ -0,0 +1,271 @@ |
|||||
|
using System; |
||||
|
using System.Collections; |
||||
|
using System.Collections.ObjectModel; |
||||
|
using System.Threading; |
||||
|
using System.Threading.Tasks; |
||||
|
using Avalonia.Animation; |
||||
|
using Avalonia.Controls.Presenters; |
||||
|
using Avalonia.Controls.Primitives; |
||||
|
using Avalonia.Controls.Templates; |
||||
|
using Avalonia.Layout; |
||||
|
using Avalonia.UnitTests; |
||||
|
using Avalonia.VisualTree; |
||||
|
using Moq; |
||||
|
using Xunit; |
||||
|
|
||||
|
#nullable enable |
||||
|
|
||||
|
namespace Avalonia.Controls.UnitTests |
||||
|
{ |
||||
|
public class VirtualizingCarouselPanelTests |
||||
|
{ |
||||
|
[Fact] |
||||
|
public void Initial_Item_Is_Displayed() |
||||
|
{ |
||||
|
using var app = Start(); |
||||
|
var items = new[] { "foo", "bar" }; |
||||
|
var (target, _) = CreateTarget(items); |
||||
|
|
||||
|
Assert.Single(target.Children); |
||||
|
var container = Assert.IsType<ContentPresenter>(target.Children[0]); |
||||
|
Assert.Equal("foo", container.Content); |
||||
|
} |
||||
|
|
||||
|
[Fact] |
||||
|
public void Displays_Next_Item() |
||||
|
{ |
||||
|
using var app = Start(); |
||||
|
var items = new[] { "foo", "bar" }; |
||||
|
var (target, carousel) = CreateTarget(items); |
||||
|
|
||||
|
carousel.SelectedIndex = 1; |
||||
|
Layout(target); |
||||
|
|
||||
|
Assert.Single(target.Children); |
||||
|
var container = Assert.IsType<ContentPresenter>(target.Children[0]); |
||||
|
Assert.Equal("bar", container.Content); |
||||
|
} |
||||
|
|
||||
|
[Fact] |
||||
|
public void Handles_Inserted_Item() |
||||
|
{ |
||||
|
using var app = Start(); |
||||
|
var items = new ObservableCollection<string> { "foo", "bar" }; |
||||
|
var (target, carousel) = CreateTarget(items); |
||||
|
var container = Assert.IsType<ContentPresenter>(target.Children[0]); |
||||
|
|
||||
|
items.Insert(0, "baz"); |
||||
|
Layout(target); |
||||
|
|
||||
|
Assert.Single(target.Children); |
||||
|
Assert.Same(container, target.Children[0]); |
||||
|
Assert.Equal("foo", container.Content); |
||||
|
} |
||||
|
|
||||
|
[Fact] |
||||
|
public void Handles_Removed_Item() |
||||
|
{ |
||||
|
using var app = Start(); |
||||
|
var items = new ObservableCollection<string> { "foo", "bar" }; |
||||
|
var (target, carousel) = CreateTarget(items); |
||||
|
var container = Assert.IsType<ContentPresenter>(target.Children[0]); |
||||
|
|
||||
|
items.RemoveAt(0); |
||||
|
Layout(target); |
||||
|
|
||||
|
Assert.Single(target.Children); |
||||
|
Assert.Same(container, target.Children[0]); |
||||
|
Assert.Equal("bar", container.Content); |
||||
|
} |
||||
|
|
||||
|
[Fact] |
||||
|
public void Handles_Replaced_Item() |
||||
|
{ |
||||
|
using var app = Start(); |
||||
|
var items = new ObservableCollection<string> { "foo", "bar" }; |
||||
|
var (target, carousel) = CreateTarget(items); |
||||
|
var container = Assert.IsType<ContentPresenter>(target.Children[0]); |
||||
|
|
||||
|
items[0] = "baz"; |
||||
|
Layout(target); |
||||
|
|
||||
|
Assert.Single(target.Children); |
||||
|
Assert.Same(container, target.Children[0]); |
||||
|
Assert.Equal("baz", container.Content); |
||||
|
} |
||||
|
|
||||
|
[Fact] |
||||
|
public void Handles_Moved_Item() |
||||
|
{ |
||||
|
using var app = Start(); |
||||
|
var items = new ObservableCollection<string> { "foo", "bar" }; |
||||
|
var (target, carousel) = CreateTarget(items); |
||||
|
var container = Assert.IsType<ContentPresenter>(target.Children[0]); |
||||
|
|
||||
|
items.Move(0, 1); |
||||
|
Layout(target); |
||||
|
|
||||
|
Assert.Single(target.Children); |
||||
|
Assert.Same(container, target.Children[0]); |
||||
|
Assert.Equal("bar", container.Content); |
||||
|
} |
||||
|
|
||||
|
public class Transitions |
||||
|
{ |
||||
|
[Fact] |
||||
|
public void Initial_Item_Does_Not_Start_Transition() |
||||
|
{ |
||||
|
using var app = Start(); |
||||
|
var items = new Control[] { new Button(), new Canvas() }; |
||||
|
var transition = new Mock<IPageTransition>(); |
||||
|
var (target, _) = CreateTarget(items, transition.Object); |
||||
|
|
||||
|
transition.Verify(x => x.Start( |
||||
|
It.IsAny<Visual>(), |
||||
|
It.IsAny<Visual>(), |
||||
|
It.IsAny<bool>(), |
||||
|
It.IsAny<CancellationToken>()), |
||||
|
Times.Never); |
||||
|
} |
||||
|
|
||||
|
[Fact] |
||||
|
public void Changing_SelectedIndex_Starts_Transition() |
||||
|
{ |
||||
|
using var app = Start(); |
||||
|
var items = new Control[] { new Button(), new Canvas() }; |
||||
|
var transition = new Mock<IPageTransition>(); |
||||
|
var (target, carousel) = CreateTarget(items, transition.Object); |
||||
|
|
||||
|
carousel.SelectedIndex = 1; |
||||
|
Layout(target); |
||||
|
|
||||
|
transition.Verify(x => x.Start( |
||||
|
items[0], |
||||
|
items[1], |
||||
|
true, |
||||
|
It.IsAny<CancellationToken>()), |
||||
|
Times.Once); |
||||
|
} |
||||
|
|
||||
|
[Fact] |
||||
|
public void TransitionFrom_Control_Is_Recycled_When_Transition_Completes() |
||||
|
{ |
||||
|
using var app = Start(); |
||||
|
using var sync = UnitTestSynchronizationContext.Begin(); |
||||
|
var items = new Control[] { new Button(), new Canvas() }; |
||||
|
var transition = new Mock<IPageTransition>(); |
||||
|
var (target, carousel) = CreateTarget(items, transition.Object); |
||||
|
var transitionTask = new TaskCompletionSource(); |
||||
|
|
||||
|
transition.Setup(x => x.Start( |
||||
|
items[0], |
||||
|
items[1], |
||||
|
true, |
||||
|
It.IsAny<CancellationToken>())) |
||||
|
.Returns(() => transitionTask.Task); |
||||
|
|
||||
|
carousel.SelectedIndex = 1; |
||||
|
Layout(target); |
||||
|
|
||||
|
Assert.Equal(items, target.Children); |
||||
|
Assert.All(items, x => Assert.True(x.IsVisible)); |
||||
|
|
||||
|
transitionTask.SetResult(); |
||||
|
sync.ExecutePostedCallbacks(); |
||||
|
|
||||
|
Assert.Equal(items, target.Children); |
||||
|
Assert.False(items[0].IsVisible); |
||||
|
Assert.True(items[1].IsVisible); |
||||
|
} |
||||
|
|
||||
|
[Fact] |
||||
|
public void Existing_Transition_Is_Canceled_If_Interrupted() |
||||
|
{ |
||||
|
using var app = Start(); |
||||
|
using var sync = UnitTestSynchronizationContext.Begin(); |
||||
|
var items = new Control[] { new Button(), new Canvas() }; |
||||
|
var transition = new Mock<IPageTransition>(); |
||||
|
var (target, carousel) = CreateTarget(items, transition.Object); |
||||
|
var transitionTask = new TaskCompletionSource(); |
||||
|
CancellationToken? cancelationToken = null; |
||||
|
|
||||
|
transition.Setup(x => x.Start( |
||||
|
items[0], |
||||
|
items[1], |
||||
|
true, |
||||
|
It.IsAny<CancellationToken>())) |
||||
|
.Callback<Visual, Visual, bool, CancellationToken>((_, _, _, c) => cancelationToken = c) |
||||
|
.Returns(() => transitionTask.Task); |
||||
|
|
||||
|
carousel.SelectedIndex = 1; |
||||
|
Layout(target); |
||||
|
|
||||
|
Assert.NotNull(cancelationToken); |
||||
|
Assert.False(cancelationToken!.Value.IsCancellationRequested); |
||||
|
|
||||
|
carousel.SelectedIndex = 0; |
||||
|
Layout(target); |
||||
|
|
||||
|
Assert.True(cancelationToken!.Value.IsCancellationRequested); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
private static IDisposable Start() => UnitTestApplication.Start(TestServices.MockPlatformRenderInterface); |
||||
|
|
||||
|
private static (VirtualizingCarouselPanel, Carousel) CreateTarget( |
||||
|
IEnumerable items, |
||||
|
IPageTransition? transition = null) |
||||
|
{ |
||||
|
var carousel = new Carousel |
||||
|
{ |
||||
|
Items = items, |
||||
|
Template = CarouselTemplate(), |
||||
|
PageTransition = transition, |
||||
|
}; |
||||
|
|
||||
|
var root = new TestRoot(carousel); |
||||
|
root.LayoutManager.ExecuteInitialLayoutPass(); |
||||
|
return ((VirtualizingCarouselPanel)carousel.Presenter!.Panel!, carousel); |
||||
|
} |
||||
|
|
||||
|
private static IControlTemplate CarouselTemplate() |
||||
|
{ |
||||
|
return new FuncControlTemplate((c, ns) => |
||||
|
new ScrollViewer |
||||
|
{ |
||||
|
Name = "PART_ScrollViewer", |
||||
|
Template = ScrollViewerTemplate(), |
||||
|
HorizontalScrollBarVisibility = ScrollBarVisibility.Hidden, |
||||
|
VerticalScrollBarVisibility = ScrollBarVisibility.Hidden, |
||||
|
Content = new ItemsPresenter |
||||
|
{ |
||||
|
Name = "PART_ItemsPresenter", |
||||
|
[~ItemsPresenter.ItemsPanelProperty] = c[~ItemsControl.ItemsPanelProperty], |
||||
|
}.RegisterInNameScope(ns) |
||||
|
}.RegisterInNameScope(ns)); |
||||
|
} |
||||
|
|
||||
|
private static FuncControlTemplate ScrollViewerTemplate() |
||||
|
{ |
||||
|
return new FuncControlTemplate<ScrollViewer>((parent, scope) => |
||||
|
new Panel |
||||
|
{ |
||||
|
Children = |
||||
|
{ |
||||
|
new ScrollContentPresenter |
||||
|
{ |
||||
|
Name = "PART_ContentPresenter", |
||||
|
[~ScrollContentPresenter.ContentProperty] = parent.GetObservable(ScrollViewer.ContentProperty).ToBinding(), |
||||
|
[~~ScrollContentPresenter.ExtentProperty] = parent[~~ScrollViewer.ExtentProperty], |
||||
|
[~~ScrollContentPresenter.OffsetProperty] = parent[~~ScrollViewer.OffsetProperty], |
||||
|
[~~ScrollContentPresenter.ViewportProperty] = parent[~~ScrollViewer.ViewportProperty], |
||||
|
[~ScrollContentPresenter.CanHorizontallyScrollProperty] = parent[~ScrollViewer.CanHorizontallyScrollProperty], |
||||
|
[~ScrollContentPresenter.CanVerticallyScrollProperty] = parent[~ScrollViewer.CanVerticallyScrollProperty], |
||||
|
}.RegisterInNameScope(scope), |
||||
|
} |
||||
|
}); |
||||
|
} |
||||
|
|
||||
|
private static void Layout(Control c) => ((ILayoutRoot)c.GetVisualRoot()!).LayoutManager.ExecuteLayoutPass(); |
||||
|
} |
||||
|
} |
||||
@ -1,11 +1,10 @@ |
|||||
using System; |
using System; |
||||
using System.Collections.Generic; |
using System.Collections.Generic; |
||||
using System.Reactive.Disposables; |
|
||||
using System.Threading; |
using System.Threading; |
||||
|
|
||||
namespace Avalonia.Base.UnitTests.Data |
namespace Avalonia.UnitTests |
||||
{ |
{ |
||||
internal sealed class UnitTestSynchronizationContext : SynchronizationContext |
public sealed class UnitTestSynchronizationContext : SynchronizationContext |
||||
{ |
{ |
||||
readonly List<Tuple<SendOrPostCallback, object>> _postedCallbacks = |
readonly List<Tuple<SendOrPostCallback, object>> _postedCallbacks = |
||||
new List<Tuple<SendOrPostCallback, object>>(); |
new List<Tuple<SendOrPostCallback, object>>(); |
||||
Loading…
Reference in new issue