Added ContextMenu/Flyout animations. Added platform services. Use bitmap cache for all views.
This commit is contained in:
103
Harmonia.UI/Controls/AnimatedFlyout.cs
Normal file
103
Harmonia.UI/Controls/AnimatedFlyout.cs
Normal file
@@ -0,0 +1,103 @@
|
||||
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;
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user