From 6c0ede37a67531b054d51d3b153d2c2a0b2fd170 Mon Sep 17 00:00:00 2001 From: David Oliver Date: Thu, 22 Oct 2020 15:07:59 -0400 Subject: [PATCH] fix(contentdialog): [Android] Fix botom of full-size ContentDialog cut off Add an internal adjusted visible bounds which trims the status bar from the height. This allows the ContentDialog to be arranged with the correct available height. There's a case for adjusting the public-facing VisibleBounds property, but it's not clear what the exact adjustment would be and it's potentially a breaking change, and isn't required for the present case. --- .../ContentDialog/ContentDialogPopupPanel.cs | 2 +- src/Uno.UI/UI/Xaml/Window.Android.cs | 88 +++++++++++-------- .../ViewManagement/ApplicationView.Android.cs | 4 + .../UI/ViewManagement/ApplicationView.cs | 14 +++ 4 files changed, 69 insertions(+), 39 deletions(-) diff --git a/src/Uno.UI/UI/Xaml/Controls/ContentDialog/ContentDialogPopupPanel.cs b/src/Uno.UI/UI/Xaml/Controls/ContentDialog/ContentDialogPopupPanel.cs index 265d5952b30c..00128f3393e6 100644 --- a/src/Uno.UI/UI/Xaml/Controls/ContentDialog/ContentDialogPopupPanel.cs +++ b/src/Uno.UI/UI/Xaml/Controls/ContentDialog/ContentDialogPopupPanel.cs @@ -50,7 +50,7 @@ protected override Size ArrangeOverride(Size finalSize) private Rect CalculateDialogPlacement(Size desiredSize) { - var visibleBounds = ApplicationView.GetForCurrentView().VisibleBounds; + var visibleBounds = ApplicationView.GetForCurrentView().TrueVisibleBounds; // Make sure the desiredSize fits in visibleBounds if (desiredSize.Width > visibleBounds.Width) diff --git a/src/Uno.UI/UI/Xaml/Window.Android.cs b/src/Uno.UI/UI/Xaml/Window.Android.cs index e8b003e25f44..47a477ac3955 100644 --- a/src/Uno.UI/UI/Xaml/Window.Android.cs +++ b/src/Uno.UI/UI/Xaml/Window.Android.cs @@ -95,49 +95,63 @@ internal void RaiseNativeSizeChanged() var newBounds = ViewHelper.PhysicalToLogicalPixels(new Rect(0, 0, fullScreenMetrics.WidthPixels, fullScreenMetrics.HeightPixels)); - var statusBarSizeExcluded = GetLogicalStatusBarSizeExcluded(); - var navigationBarSizeExcluded = GetLogicalNavigationBarSizeExcluded(); + var statusBarSize = GetLogicalStatusBarSize(); + + var statusBarSizeExcluded = IsStatusBarTranslucent() ? + // The real metrics excluded the StatusBar only if it is plain. + // We want to subtract it if it is translucent. Otherwise, it will be like we subtract it twice. + statusBarSize : + 0; + var navigationBarSizeExcluded = GetLogicalNavigationBarSizeExcluded(); // Actually, we need to check visibility of nav bar and status bar since the insets don't UpdateInsetsWithVisibilities(); - var topHeightExcluded = Math.Max(Insets.Top, statusBarSizeExcluded); - var orientation = DisplayInformation.GetForCurrentView().CurrentOrientation; - var newVisibleBounds = new Rect(); - switch (orientation) + Rect CalculateVisibleBounds(double excludedStatusBarHeight) { - // StatusBar on top, NavigationBar on right - case DisplayOrientations.Landscape: - newVisibleBounds = new Rect( - x: newBounds.X + Insets.Left, - y: newBounds.Y + topHeightExcluded, - width: newBounds.Width - (Insets.Left + Math.Max(Insets.Right, navigationBarSizeExcluded)), - height: newBounds.Height - topHeightExcluded - Insets.Bottom - ); - break; - // StatusBar on top, NavigationBar on left - case DisplayOrientations.LandscapeFlipped: - newVisibleBounds = new Rect( - x: newBounds.X + Math.Max(Insets.Left, navigationBarSizeExcluded), - y: newBounds.Y + topHeightExcluded, - width: newBounds.Width - (Math.Max(Insets.Left, navigationBarSizeExcluded) + Insets.Right), - height: newBounds.Height - topHeightExcluded - Insets.Bottom - ); - break; - // StatusBar on top, NavigationBar on bottom - default: - newVisibleBounds = new Rect( - x: newBounds.X + Insets.Left, - y: newBounds.Y + topHeightExcluded, - width: newBounds.Width - (Insets.Left + Insets.Right), - height: newBounds.Height - topHeightExcluded - Math.Max(Insets.Bottom, navigationBarSizeExcluded) - ); - break; + var topHeightExcluded = Math.Max(Insets.Top, excludedStatusBarHeight); + var newVisibleBounds = new Rect(); + + switch (orientation) + { + // StatusBar on top, NavigationBar on right + case DisplayOrientations.Landscape: + newVisibleBounds = new Rect( + x: newBounds.X + Insets.Left, + y: newBounds.Y + topHeightExcluded, + width: newBounds.Width - (Insets.Left + Math.Max(Insets.Right, navigationBarSizeExcluded)), + height: newBounds.Height - topHeightExcluded - Insets.Bottom + ); + break; + // StatusBar on top, NavigationBar on left + case DisplayOrientations.LandscapeFlipped: + newVisibleBounds = new Rect( + x: newBounds.X + Math.Max(Insets.Left, navigationBarSizeExcluded), + y: newBounds.Y + topHeightExcluded, + width: newBounds.Width - (Math.Max(Insets.Left, navigationBarSizeExcluded) + Insets.Right), + height: newBounds.Height - topHeightExcluded - Insets.Bottom + ); + break; + // StatusBar on top, NavigationBar on bottom + default: + newVisibleBounds = new Rect( + x: newBounds.X + Insets.Left, + y: newBounds.Y + topHeightExcluded, + width: newBounds.Width - (Insets.Left + Insets.Right), + height: newBounds.Height - topHeightExcluded - Math.Max(Insets.Bottom, navigationBarSizeExcluded) + ); + break; + } + + return newVisibleBounds; } - ApplicationView.GetForCurrentView()?.SetVisibleBounds(newVisibleBounds); + var visibleBounds = CalculateVisibleBounds(statusBarSizeExcluded); + var trueVisibleBounds = CalculateVisibleBounds(statusBarSize); + ApplicationView.GetForCurrentView()?.SetVisibleBounds(visibleBounds); + ApplicationView.GetForCurrentView()?.SetTrueVisibleBounds(trueVisibleBounds); if (Bounds != newBounds) { @@ -194,13 +208,11 @@ internal void UpdateInsetsWithVisibilities() Insets = newInsets; } - private double GetLogicalStatusBarSizeExcluded() + private double GetLogicalStatusBarSize() { var logicalStatusBarHeight = 0d; - // The real metrics excluded the StatusBar only if it is plain. - // We want to subtract it if it is translucent. Otherwise, it will be like we subtract it twice. - if (IsStatusBarVisible() && IsStatusBarTranslucent()) + if (IsStatusBarVisible()) { var resourceId = Android.Content.Res.Resources.System.GetIdentifier("status_bar_height", "dimen", "android"); if (resourceId > 0) diff --git a/src/Uno.UWP/UI/ViewManagement/ApplicationView.Android.cs b/src/Uno.UWP/UI/ViewManagement/ApplicationView.Android.cs index ca3121b96be2..38c7db5fcd9c 100644 --- a/src/Uno.UWP/UI/ViewManagement/ApplicationView.Android.cs +++ b/src/Uno.UWP/UI/ViewManagement/ApplicationView.Android.cs @@ -48,6 +48,10 @@ public string Title } } + private Rect _trueVisibleBounds; + + internal void SetTrueVisibleBounds(Rect trueVisibleBounds) => _trueVisibleBounds = trueVisibleBounds; + public bool TryEnterFullScreenMode() { CoreDispatcher.CheckThreadAccess(); diff --git a/src/Uno.UWP/UI/ViewManagement/ApplicationView.cs b/src/Uno.UWP/UI/ViewManagement/ApplicationView.cs index eae46e7a13ab..285603f67d99 100644 --- a/src/Uno.UWP/UI/ViewManagement/ApplicationView.cs +++ b/src/Uno.UWP/UI/ViewManagement/ApplicationView.cs @@ -50,6 +50,20 @@ public bool SetDesiredBoundsMode(global::Windows.UI.ViewManagement.ApplicationVi public Foundation.Rect VisibleBounds { get; private set; } + /// + /// All other platforms: equivalent to . + /// + /// Android: returns the visible bounds taking the status bar into account. The status bar is not removed from + /// on Android when it's opaque, on the grounds that the root managed view is already arranged below the status bar in y-direction by + /// default (unlike iOS), but in some cases the correct total height is needed, hence this property. + /// + internal Rect TrueVisibleBounds => +#if __ANDROID__ + _trueVisibleBounds; +#else + VisibleBounds; +#endif + public event global::Windows.Foundation.TypedEventHandler VisibleBoundsChanged; [global::Uno.NotImplemented]