using System; using Gallery.UI.Theme; using Gallery.Utils; using Xamarin.Essentials; using Xamarin.Forms; namespace Gallery.UI { public class AdaptedPage : ContentPage { public static readonly BindableProperty PageTopMarginProperty = BindableProperty.Create( nameof(PageTopMargin), typeof(Thickness), typeof(AdaptedPage)); public static readonly BindableProperty PanelTopMarginProperty = BindableProperty.Create( nameof(PanelTopMargin), typeof(Thickness), typeof(AdaptedPage)); public Thickness PageTopMargin { get => (Thickness)GetValue(PageTopMarginProperty); protected set => SetValue(PageTopMarginProperty, value); } public Thickness PanelTopMargin { get => (Thickness)GetValue(PanelTopMarginProperty); protected set => SetValue(PanelTopMarginProperty, value); } public event EventHandler Load; public event EventHandler Unload; protected static readonly bool isPhone = DeviceInfo.Idiom == DeviceIdiom.Phone; public AdaptedPage() { SetDynamicResource(Screen.StatusBarStyleProperty, ThemeBase.StatusBarStyle); Shell.SetNavBarHasShadow(this, true); } public virtual void OnLoad() { Load?.Invoke(this, EventArgs.Empty); } public virtual void OnUnload() { Unload?.Invoke(this, EventArgs.Empty); } #if __IOS__ public virtual void OnOrientationChanged(bool landscape) { var oldMargin = PageTopMargin; var oldPanelMargin = PanelTopMargin; Thickness newMargin; Thickness newPanelMargin; if (StyleDefinition.IsFullscreenDevice) { var iPhone12 = #if DEBUG DeviceInfo.Name.StartsWith("iPhone 12") || #endif DeviceInfo.Model.StartsWith("iPhone13,"); newMargin = landscape ? AppShell.NavigationBarOffset : AppShell.TotalBarOffset; newPanelMargin = landscape ? AppShell.HalfNavigationBarOffset : AppShell.NavigationBarOffset; } else if (isPhone) { newMargin = landscape ? StyleDefinition.TopOffset32 : AppShell.TotalBarOffset; newPanelMargin = landscape ? StyleDefinition.TopOffset16 : StyleDefinition.TopOffset32; } else { // iPad newMargin = AppShell.TotalBarOffset; newPanelMargin = StyleDefinition.TopOffset37; } if (oldMargin != newMargin) { PageTopMargin = newMargin; OnPageTopMarginChanged(oldMargin, newMargin); } if (oldPanelMargin != newPanelMargin) { PanelTopMargin = newPanelMargin; } } protected virtual void OnPageTopMarginChanged(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; #if DEBUG if (start != 0) { App.DebugPrint($"{element.GetType()}, margin-top from {element.Margin.Top} to {margin.Top}"); } #endif element.Margin = m; if (start > 0 && animate) { ViewExtensions.CancelAnimations(element); 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; }); } } #endif protected void Start(Action action) { if (Tap.IsBusy) { #if LOG App.DebugPrint("gesture recognizer is now busy..."); #endif 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 object(); private static readonly Tap _instance = new Tap(); private Tap() { } public static Tap Start() { lock (sync) { _instance.isBusy = true; } return _instance; } private bool isBusy = false; public void Dispose() { isBusy = false; } } } public class ThicknessEventArgs : EventArgs { public Thickness OldMargin { get; set; } public Thickness NewMargin { get; set; } } }