You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
167 lines
4.8 KiB
167 lines
4.8 KiB
using System; |
|
using System.Collections; |
|
using System.Diagnostics.CodeAnalysis; |
|
using Avalonia; |
|
using Avalonia.Controls; |
|
using Avalonia.Controls.Primitives; |
|
using Avalonia.Controls.Templates; |
|
using FluentAvalonia.UI.Controls; |
|
using FluentAvalonia.UI.Media.Animation; |
|
using FluentAvalonia.UI.Navigation; |
|
using StabilityMatrix.Avalonia.Animations; |
|
using StabilityMatrix.Core.Extensions; |
|
|
|
namespace StabilityMatrix.Avalonia.Controls; |
|
|
|
[SuppressMessage("ReSharper", "MemberCanBePrivate.Global")] |
|
public class FrameCarousel : SelectingItemsControl |
|
{ |
|
public static readonly StyledProperty<IDataTemplate?> ContentTemplateProperty = AvaloniaProperty.Register<FrameCarousel, IDataTemplate?>( |
|
"ContentTemplate"); |
|
|
|
public IDataTemplate? ContentTemplate |
|
{ |
|
get => GetValue(ContentTemplateProperty); |
|
set => SetValue(ContentTemplateProperty, value); |
|
} |
|
|
|
public static readonly StyledProperty<Type> SourcePageTypeProperty = AvaloniaProperty.Register<FrameCarousel, Type>( |
|
"SourcePageType"); |
|
|
|
public Type SourcePageType |
|
{ |
|
get => GetValue(SourcePageTypeProperty); |
|
set => SetValue(SourcePageTypeProperty, value); |
|
} |
|
|
|
private Frame? frame; |
|
private int previousIndex = -1; |
|
|
|
private static readonly FrameNavigationOptions ForwardNavigationOptions |
|
= new() |
|
{ |
|
TransitionInfoOverride = new BetterSlideNavigationTransition |
|
{ |
|
Effect = SlideNavigationTransitionEffect.FromRight, |
|
FromHorizontalOffset = 200 |
|
} |
|
}; |
|
|
|
private static readonly FrameNavigationOptions BackNavigationOptions |
|
= new() |
|
{ |
|
TransitionInfoOverride = new BetterSlideNavigationTransition |
|
{ |
|
Effect = SlideNavigationTransitionEffect.FromLeft, |
|
FromHorizontalOffset = 200 |
|
} |
|
}; |
|
|
|
private static readonly FrameNavigationOptions DirectionlessNavigationOptions |
|
= new() |
|
{ |
|
TransitionInfoOverride = new SuppressNavigationTransitionInfo() |
|
}; |
|
|
|
/// <inheritdoc /> |
|
protected override void OnApplyTemplate(TemplateAppliedEventArgs e) |
|
{ |
|
base.OnApplyTemplate(e); |
|
|
|
frame = e.NameScope.Find<Frame>("PART_Frame") |
|
?? throw new NullReferenceException("Frame not found"); |
|
|
|
frame.NavigationPageFactory = new FrameNavigationFactory(SourcePageType); |
|
|
|
if (SelectedItem is not null) |
|
{ |
|
frame.NavigateFromObject(SelectedItem, DirectionlessNavigationOptions); |
|
} |
|
} |
|
|
|
protected override void OnPropertyChanged(AvaloniaPropertyChangedEventArgs change) |
|
{ |
|
base.OnPropertyChanged(change); |
|
|
|
if (frame is null) return; |
|
|
|
if (change.Property == SelectedItemProperty) |
|
{ |
|
if (change.GetNewValue<object?>() is not { } value) return; |
|
|
|
if (SelectedIndex > previousIndex) |
|
{ |
|
// Going forward |
|
frame.NavigateFromObject(value, ForwardNavigationOptions); |
|
} |
|
else if (SelectedIndex < previousIndex) |
|
{ |
|
// Going back |
|
frame.NavigateFromObject(value, BackNavigationOptions); |
|
} |
|
else |
|
{ |
|
frame.NavigateFromObject(value, DirectionlessNavigationOptions); |
|
} |
|
|
|
previousIndex = SelectedIndex; |
|
} |
|
else if (change.Property == ItemCountProperty) |
|
{ |
|
// On item count change to 0, clear the frame cache |
|
var value = change.GetNewValue<int>(); |
|
|
|
if (value == 0) |
|
{ |
|
var pageCache = frame.GetPrivateField<IList>("_pageCache"); |
|
pageCache?.Clear(); |
|
} |
|
} |
|
} |
|
|
|
/// <summary> |
|
/// Moves to the next item in the carousel. |
|
/// </summary> |
|
public void Next() |
|
{ |
|
if (SelectedIndex < ItemCount - 1) |
|
{ |
|
++SelectedIndex; |
|
} |
|
} |
|
|
|
/// <summary> |
|
/// Moves to the previous item in the carousel. |
|
/// </summary> |
|
public void Previous() |
|
{ |
|
if (SelectedIndex > 0) |
|
{ |
|
--SelectedIndex; |
|
} |
|
} |
|
|
|
internal class FrameNavigationFactory : INavigationPageFactory |
|
{ |
|
private readonly Type _sourcePageType; |
|
|
|
public FrameNavigationFactory(Type sourcePageType) |
|
{ |
|
_sourcePageType = sourcePageType; |
|
} |
|
|
|
/// <inheritdoc /> |
|
public Control GetPage(Type srcType) |
|
{ |
|
return (Control) Activator.CreateInstance(srcType)!; |
|
} |
|
|
|
/// <inheritdoc /> |
|
public Control GetPageFromObject(object target) |
|
{ |
|
var view = GetPage(_sourcePageType); |
|
view.DataContext = target; |
|
return view; |
|
} |
|
} |
|
}
|
|
|