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 ContentTemplateProperty = AvaloniaProperty.Register( "ContentTemplate"); public IDataTemplate? ContentTemplate { get => GetValue(ContentTemplateProperty); set => SetValue(ContentTemplateProperty, value); } public static readonly StyledProperty SourcePageTypeProperty = AvaloniaProperty.Register( "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() }; /// protected override void OnApplyTemplate(TemplateAppliedEventArgs e) { base.OnApplyTemplate(e); frame = e.NameScope.Find("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() 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(); if (value == 0) { var pageCache = frame.GetPrivateField("_pageCache"); pageCache?.Clear(); } } } /// /// Moves to the next item in the carousel. /// public void Next() { if (SelectedIndex < ItemCount - 1) { ++SelectedIndex; } } /// /// Moves to the previous item in the carousel. /// public void Previous() { if (SelectedIndex > 0) { --SelectedIndex; } } internal class FrameNavigationFactory : INavigationPageFactory { private readonly Type _sourcePageType; public FrameNavigationFactory(Type sourcePageType) { _sourcePageType = sourcePageType; } /// public Control GetPage(Type srcType) { return (Control) Activator.CreateInstance(srcType)!; } /// public Control GetPageFromObject(object target) { var view = GetPage(_sourcePageType); view.DataContext = target; return view; } } }