103 lines
3.0 KiB
C#
103 lines
3.0 KiB
C#
using Avalonia;
|
|
using Avalonia.Animation;
|
|
using Avalonia.Controls;
|
|
using Avalonia.Controls.ApplicationLifetimes;
|
|
using Avalonia.Media;
|
|
using Avalonia.Rendering;
|
|
using Avalonia.Styling;
|
|
using Avalonia.VisualTree;
|
|
using System;
|
|
using System.Threading.Tasks;
|
|
|
|
namespace Harmonia.UI.Controls;
|
|
|
|
public class AnimatedMenuFlyout : MenuFlyout
|
|
{
|
|
protected override Control CreatePresenter()
|
|
{
|
|
Control presenter = base.CreatePresenter();
|
|
presenter.AttachedToVisualTree += OnPresenterAttachedToVisualTree;
|
|
|
|
return presenter;
|
|
}
|
|
|
|
private async void OnPresenterAttachedToVisualTree(object? sender, VisualTreeAttachmentEventArgs e)
|
|
{
|
|
if (sender is not MenuFlyoutPresenter menuFlyoutPresenter)
|
|
return;
|
|
|
|
await ApplyAnimationAsync(menuFlyoutPresenter);
|
|
}
|
|
|
|
private async Task ApplyAnimationAsync(MenuFlyoutPresenter presenter)
|
|
{
|
|
double translateYStart = ShouldSlideDown(presenter) ? - 100 : 100;
|
|
|
|
Animation animation = new()
|
|
{
|
|
Duration = TimeSpan.FromMilliseconds(200),
|
|
Children =
|
|
{
|
|
new KeyFrame
|
|
{
|
|
Cue = new Cue(0f),
|
|
Setters =
|
|
{
|
|
new Setter(Visual.OpacityProperty, 0.0),
|
|
new Setter(TranslateTransform.YProperty, translateYStart)
|
|
}
|
|
},
|
|
new KeyFrame
|
|
{
|
|
Cue = new Cue(1f),
|
|
Setters =
|
|
{
|
|
new Setter(Visual.OpacityProperty, 1.0),
|
|
new Setter(TranslateTransform.YProperty, 0.0)
|
|
}
|
|
}
|
|
}
|
|
};
|
|
|
|
await animation.RunAsync(presenter);
|
|
}
|
|
|
|
private static bool ShouldSlideDown(MenuFlyoutPresenter presenter)
|
|
{
|
|
if (presenter.Parent is not Control targetControl)
|
|
return true;
|
|
|
|
Rect? topLevelBounds = GetTopLevelBounds();
|
|
|
|
if (topLevelBounds == null)
|
|
return true;
|
|
|
|
Rect targetBounds = targetControl.Bounds;
|
|
|
|
double availableSpaceBelow = topLevelBounds.Value.Height - (targetBounds.Y + targetBounds.Height);
|
|
double availableSpaceAbove = targetBounds.Y;
|
|
|
|
return availableSpaceBelow >= availableSpaceAbove; // Slide down if more space below
|
|
}
|
|
|
|
private static Rect? GetTopLevelBounds()
|
|
{
|
|
//Desktop
|
|
if (Application.Current?.ApplicationLifetime is IClassicDesktopStyleApplicationLifetime desktop)
|
|
{
|
|
return desktop.MainWindow?.Bounds;
|
|
}
|
|
//Android (and iOS?)
|
|
else if (Application.Current?.ApplicationLifetime is ISingleViewApplicationLifetime singleViewPlatform)
|
|
{
|
|
IRenderRoot? visualRoot = singleViewPlatform.MainView?.GetVisualRoot();
|
|
|
|
if (visualRoot is TopLevel topLevel)
|
|
{
|
|
return topLevel.Bounds;
|
|
}
|
|
}
|
|
|
|
return null;
|
|
}
|
|
} |