149 lines
4.0 KiB
C#
149 lines
4.0 KiB
C#
using System;
|
|
using Gallery.Services;
|
|
using Gallery.Util;
|
|
using Xamarin.Essentials;
|
|
using Xamarin.Forms;
|
|
|
|
namespace Gallery.Resources.UI
|
|
{
|
|
public class AdaptedPage : ContentPage
|
|
{
|
|
public static readonly BindableProperty TopMarginProperty = BindableProperty.Create(nameof(TopMargin), typeof(Thickness), typeof(AdaptedPage));
|
|
|
|
public Thickness TopMargin
|
|
{
|
|
get => (Thickness)GetValue(TopMarginProperty);
|
|
set => SetValue(TopMarginProperty, value);
|
|
}
|
|
|
|
public event EventHandler Load;
|
|
public event EventHandler Unload;
|
|
|
|
protected static readonly bool isPhone = DeviceInfo.Idiom == DeviceIdiom.Phone;
|
|
|
|
public AdaptedPage()
|
|
{
|
|
SetDynamicResource(Screen.StatusBarStyleProperty, Theme.Theme.StatusBarStyle);
|
|
Shell.SetNavBarHasShadow(this, true);
|
|
}
|
|
|
|
public virtual void OnLoad() => Load?.Invoke(this, EventArgs.Empty);
|
|
|
|
public virtual void OnUnload() => Unload?.Invoke(this, EventArgs.Empty);
|
|
|
|
public virtual void OnOrientationChanged(bool landscape)
|
|
{
|
|
var old = TopMargin;
|
|
Thickness @new;
|
|
if (Definition.IsFullscreenDevice)
|
|
{
|
|
@new = landscape ?
|
|
AppShell.NavigationBarOffset :
|
|
AppShell.TotalBarOffset;
|
|
}
|
|
else if (isPhone)
|
|
{
|
|
@new = landscape ?
|
|
Definition.TopOffset32 :
|
|
AppShell.TotalBarOffset;
|
|
}
|
|
else
|
|
{
|
|
// iPad
|
|
@new = AppShell.TotalBarOffset;
|
|
}
|
|
if (old != @new)
|
|
{
|
|
TopMargin = @new;
|
|
OnTopMarginChanged(old, @new);
|
|
}
|
|
}
|
|
|
|
protected virtual void OnTopMarginChanged(Thickness old, Thickness @new) { }
|
|
|
|
protected override void OnSizeAllocated(double width, double height)
|
|
{
|
|
base.OnSizeAllocated(width, height);
|
|
OnOrientationChanged(width > height);
|
|
}
|
|
|
|
protected void AnimateToMargin(View element, Thickness margin, bool animate = true)
|
|
{
|
|
var m = margin;
|
|
var start = element.Margin.Top - m.Top;
|
|
element.Margin = m;
|
|
element.CancelAnimations();
|
|
if (start > 0 && animate)
|
|
{
|
|
element.Animate("margin", top =>
|
|
{
|
|
element.TranslationY = top;
|
|
},
|
|
start, 0,
|
|
#if DEBUG
|
|
length: 500,
|
|
#else
|
|
length: 300,
|
|
#endif
|
|
easing: Easing.SinInOut,
|
|
finished: (v, r) =>
|
|
{
|
|
element.TranslationY = 0;
|
|
});
|
|
}
|
|
else if (element.TranslationY != 0)
|
|
{
|
|
element.TranslationY = 0;
|
|
}
|
|
}
|
|
|
|
protected void Start(Action action)
|
|
{
|
|
if (Tap.IsBusy)
|
|
{
|
|
Log.Error($"{GetType()}.tap", "gesture recognizer is now busy...");
|
|
return;
|
|
}
|
|
using (Tap.Start())
|
|
{
|
|
action();
|
|
}
|
|
}
|
|
|
|
private class Tap : IDisposable
|
|
{
|
|
public static bool IsBusy
|
|
{
|
|
get
|
|
{
|
|
lock (sync)
|
|
{
|
|
return instance?.isBusy == true;
|
|
}
|
|
}
|
|
}
|
|
|
|
private static readonly object sync = new();
|
|
private static readonly Tap instance = new();
|
|
|
|
private Tap() { }
|
|
|
|
public static Tap Start()
|
|
{
|
|
lock (sync)
|
|
{
|
|
instance.isBusy = true;
|
|
}
|
|
return instance;
|
|
}
|
|
|
|
private bool isBusy = false;
|
|
|
|
public void Dispose()
|
|
{
|
|
isBusy = false;
|
|
}
|
|
}
|
|
}
|
|
}
|