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; } } } }