Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fix Windows Popup Position and Size #1350

Merged
merged 15 commits into from
Sep 7, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
using CommunityToolkit.Maui.Core.Views;
using Microsoft.Maui.Handlers;
using Microsoft.Maui.Platform;
using Microsoft.UI.Xaml;
using Microsoft.UI.Xaml.Controls;
using Microsoft.UI.Xaml.Controls.Primitives;

Expand Down Expand Up @@ -88,7 +89,7 @@ public static void MapColor(PopupHandler handler, IPopup view)
/// <param name="view">An instance of <see cref="IPopup"/>.</param>
public static void MapSize(PopupHandler handler, IPopup view)
{
handler.PlatformView.SetSize(view);
handler.PlatformView.SetSize(view, handler.MauiContext);
}

/// <inheritdoc/>
Expand All @@ -100,6 +101,10 @@ protected override void DisconnectHandler(Popup platformView)
parent.IsHitTestVisible = true;
platformView.IsOpen = false;
platformView.Closed -= OnClosed;
if (MauiContext is not null)
{
MauiContext.GetPlatformWindow().SizeChanged -= OnSizeChanged;
}
}

/// <inheritdoc/>
Expand All @@ -114,6 +119,10 @@ protected override void ConnectHandler(Popup platformView)
{
platformView.Closed += OnClosed;
platformView.ConfigureControl(VirtualView, MauiContext);
if (MauiContext is not null)
{
MauiContext.GetPlatformWindow().SizeChanged += OnSizeChanged;
}
base.ConnectHandler(platformView);
}

Expand All @@ -124,4 +133,13 @@ void OnClosed(object? sender, object e)
VirtualView.Handler?.Invoke(nameof(IPopup.OnDismissedByTappingOutsideOfPopup));
}
}

void OnSizeChanged(object? sender, WindowSizeChangedEventArgs e)
{
if (VirtualView is not null)
{
PopupExtensions.SetSize(PlatformView, VirtualView, MauiContext);
PopupExtensions.SetLayout(PlatformView, VirtualView, MauiContext);
}
}
}
125 changes: 100 additions & 25 deletions src/CommunityToolkit.Maui.Core/Views/Popup/PopupExtensions.windows.cs
Original file line number Diff line number Diff line change
Expand Up @@ -54,7 +54,7 @@ public static void ConfigureControl(this Popup mauiPopup, IPopup popup, IMauiCon
mauiPopup.Child = handler.VirtualView.Content?.ToPlatform(mauiContext);
}

mauiPopup.SetSize(popup);
mauiPopup.SetSize(popup, mauiContext);
mauiPopup.SetLayout(popup, mauiContext);
}

Expand All @@ -63,41 +63,58 @@ public static void ConfigureControl(this Popup mauiPopup, IPopup popup, IMauiCon
/// </summary>
/// <param name="mauiPopup">An instance of <see cref="Popup"/>.</param>
/// <param name="popup">An instance of <see cref="IPopup"/>.</param>
public static void SetSize(this Popup mauiPopup, IPopup popup)
/// <param name="mauiContext">An instance of <see cref="IMauiContext"/>.</param>
public static void SetSize(this Popup mauiPopup, IPopup popup, IMauiContext? mauiContext)
VladislavAntonyuk marked this conversation as resolved.
Show resolved Hide resolved
{
ArgumentNullException.ThrowIfNull(mauiContext);
ArgumentNullException.ThrowIfNull(popup.Content);

const double defaultBorderThickness = 0;
const double defaultSize = 600;

var standardSize = new Size { Width = defaultSize, Height = defaultSize / 2 };
var popupParent = mauiContext.GetPlatformWindow();
var currentSize = new Size { Width = defaultSize, Height = defaultSize / 2 };

var currentSize = popup.Size != default ? popup.Size : standardSize;

if (popup.Content is not null && popup.Size == default)
if (popup.Size.IsZero)
{
var content = popup.Content;
// There are some situations when the Width and Height values will be NaN
// normally when the dev doesn't set the HeightRequest and WidthRequest
// and we can't use comparison on those, so the only to prevent the application to crash
// is using this try/catch
try
if (double.IsNaN(popup.Content.Width) || (double.IsNaN(popup.Content.Height)))
{
currentSize = new Size(content.Width, content.Height);
currentSize = popup.Content.Measure(popupParent.Bounds.Width, popupParent.Bounds.Height);

if (double.IsNaN(popup.Content.Width))
{
currentSize.Width = popup.HorizontalOptions == LayoutAlignment.Fill ? popupParent.Bounds.Width : currentSize.Width;
}
if (double.IsNaN(popup.Content.Height))
{
currentSize.Height = popup.VerticalOptions == LayoutAlignment.Fill ? popupParent.Bounds.Height : currentSize.Height;
}
}
catch (ArgumentException)
else
{
currentSize.Width = popup.Content.Width;
currentSize.Height = popup.Content.Height;
}
}

if (popup.Parent is IView popupParent)
else
{
currentSize.Width = Math.Min(currentSize.Width, popupParent.Frame.Width);
currentSize.Height = Math.Min(currentSize.Height, popupParent.Frame.Height);
currentSize.Width = popup.Size.Width;
currentSize.Height = popup.Size.Height;
}

currentSize.Width = Math.Min(currentSize.Width, popupParent.Bounds.Width);
currentSize.Height = Math.Min(currentSize.Height, popupParent.Bounds.Height);

mauiPopup.Width = currentSize.Width;
mauiPopup.Height = currentSize.Height;
mauiPopup.MinWidth = mauiPopup.MaxWidth = currentSize.Width + (defaultBorderThickness * 2);
mauiPopup.MinHeight = mauiPopup.MaxHeight = currentSize.Height + (defaultBorderThickness * 2);

if (mauiPopup.Child is FrameworkElement control)
{
control.Width = mauiPopup.Width;
control.Height = mauiPopup.Height;
}
}

/// <summary>
Expand All @@ -109,10 +126,12 @@ public static void SetSize(this Popup mauiPopup, IPopup popup)
public static void SetLayout(this Popup mauiPopup, IPopup popup, IMauiContext? mauiContext)
{
ArgumentNullException.ThrowIfNull(mauiContext);
var popupParent = popup.Parent as IView;
popup.Content?.Measure(double.PositiveInfinity, double.PositiveInfinity);
var contentSize = popup.Content?.ToPlatform(mauiContext).DesiredSize ?? Windows.Foundation.Size.Empty;
var popupParentFrame = popupParent?.Frame ?? new Rect(0, 0, contentSize.Width, contentSize.Height);
ArgumentNullException.ThrowIfNull(popup.Content);

var popupParent = mauiContext.GetPlatformWindow();
popup.Content.Measure(double.PositiveInfinity, double.PositiveInfinity);
var contentSize = popup.Content.ToPlatform(mauiContext).DesiredSize;
var popupParentFrame = popupParent.Bounds;

var verticalOptions = popup.VerticalOptions;
var horizontalOptions = popup.HorizontalOptions;
Expand Down Expand Up @@ -144,26 +163,74 @@ public static void SetLayout(this Popup mauiPopup, IPopup popup, IMauiContext? m
{
mauiPopup.DesiredPlacement = PopupPlacementMode.BottomEdgeAlignedRight;
mauiPopup.HorizontalOffset = (popupParentFrame.Width - contentSize.Width);
mauiPopup.VerticalOffset = popupParentFrame.Height + contentSize.Height / 2;
mauiPopup.VerticalOffset = popupParentFrame.Height - contentSize.Height;
}
else if (IsBottom(verticalOptions, horizontalOptions))
{
mauiPopup.DesiredPlacement = PopupPlacementMode.Bottom;
mauiPopup.HorizontalOffset = (popupParentFrame.Width - contentSize.Width) / 2;
mauiPopup.VerticalOffset = popupParentFrame.Height + contentSize.Height / 2;
mauiPopup.VerticalOffset = popupParentFrame.Height - contentSize.Height;
}
else if (IsBottomLeft(verticalOptions, horizontalOptions))
{
mauiPopup.DesiredPlacement = PopupPlacementMode.BottomEdgeAlignedLeft;
mauiPopup.HorizontalOffset = 0;
mauiPopup.VerticalOffset = popupParentFrame.Height + contentSize.Height / 2;
mauiPopup.VerticalOffset = popupParentFrame.Height - contentSize.Height;
}
else if (IsLeft(verticalOptions, horizontalOptions))
{
mauiPopup.DesiredPlacement = PopupPlacementMode.Left;
mauiPopup.HorizontalOffset = 0;
mauiPopup.VerticalOffset = (popupParentFrame.Height - contentSize.Height) / 2;
}
else if (IsCenter(verticalOptions, horizontalOptions))
{
mauiPopup.DesiredPlacement = PopupPlacementMode.Auto;
mauiPopup.HorizontalOffset = (popupParentFrame.Width - contentSize.Width) / 2;
mauiPopup.VerticalOffset = (popupParentFrame.Height - contentSize.Height) / 2;
}
else if (IsFillLeft(verticalOptions, horizontalOptions))
{
mauiPopup.DesiredPlacement = PopupPlacementMode.Auto;
mauiPopup.HorizontalOffset = 0;
mauiPopup.VerticalOffset = (popupParentFrame.Height - contentSize.Height) / 2;
}
else if (IsFillCenter(verticalOptions, horizontalOptions))
{
mauiPopup.DesiredPlacement = PopupPlacementMode.Auto;
mauiPopup.HorizontalOffset = (popupParentFrame.Width - contentSize.Width) / 2;
mauiPopup.VerticalOffset = (popupParentFrame.Height - contentSize.Height) / 2;
}
else if (IsFillRight(verticalOptions, horizontalOptions))
{
mauiPopup.DesiredPlacement = PopupPlacementMode.Auto;
mauiPopup.HorizontalOffset = (popupParentFrame.Width - contentSize.Width);
mauiPopup.VerticalOffset = (popupParentFrame.Height - contentSize.Height) / 2;
}
else if (IsTopFill(verticalOptions, horizontalOptions))
{
mauiPopup.DesiredPlacement = PopupPlacementMode.Auto;
mauiPopup.HorizontalOffset = (popupParentFrame.Width - contentSize.Width) / 2;
mauiPopup.VerticalOffset = 0;
}
else if (IsCenterFill(verticalOptions, horizontalOptions))
{
mauiPopup.DesiredPlacement = PopupPlacementMode.Auto;
mauiPopup.HorizontalOffset = (popupParentFrame.Width - contentSize.Width) / 2;
mauiPopup.VerticalOffset = (popupParentFrame.Height - contentSize.Height) / 2;
}
else if (IsBottomFill(verticalOptions, horizontalOptions))
{
mauiPopup.DesiredPlacement = PopupPlacementMode.Auto;
mauiPopup.HorizontalOffset = (popupParentFrame.Width - contentSize.Width) / 2;
mauiPopup.VerticalOffset = popupParentFrame.Height - contentSize.Height;
}
else if (IsFill(verticalOptions, horizontalOptions))
{
mauiPopup.DesiredPlacement = PopupPlacementMode.Auto;
mauiPopup.HorizontalOffset = (popupParentFrame.Width - contentSize.Width) / 2;
mauiPopup.VerticalOffset = (popupParentFrame.Height - contentSize.Height) / 2;
}
else if (popup.Anchor is null)
{
mauiPopup.DesiredPlacement = PopupPlacementMode.Auto;
Expand All @@ -183,5 +250,13 @@ public static void SetLayout(this Popup mauiPopup, IPopup popup, IMauiContext? m
static bool IsBottom(LayoutAlignment verticalOptions, LayoutAlignment horizontalOptions) => verticalOptions == LayoutAlignment.End && horizontalOptions == LayoutAlignment.Center;
static bool IsBottomLeft(LayoutAlignment verticalOptions, LayoutAlignment horizontalOptions) => verticalOptions == LayoutAlignment.End && horizontalOptions == LayoutAlignment.Start;
static bool IsLeft(LayoutAlignment verticalOptions, LayoutAlignment horizontalOptions) => verticalOptions == LayoutAlignment.Center && horizontalOptions == LayoutAlignment.Start;
static bool IsCenter(LayoutAlignment verticalOptions, LayoutAlignment horizontalOptions) => verticalOptions == LayoutAlignment.Center && horizontalOptions == LayoutAlignment.Center;
static bool IsFillLeft(LayoutAlignment verticalOptions, LayoutAlignment horizontalOptions) => verticalOptions == LayoutAlignment.Fill && horizontalOptions == LayoutAlignment.Start;
static bool IsFillCenter(LayoutAlignment verticalOptions, LayoutAlignment horizontalOptions) => verticalOptions == LayoutAlignment.Fill && horizontalOptions == LayoutAlignment.Center;
static bool IsFillRight(LayoutAlignment verticalOptions, LayoutAlignment horizontalOptions) => verticalOptions == LayoutAlignment.Fill && horizontalOptions == LayoutAlignment.End;
static bool IsTopFill(LayoutAlignment verticalOptions, LayoutAlignment horizontalOptions) => verticalOptions == LayoutAlignment.Start && horizontalOptions == LayoutAlignment.Fill;
static bool IsCenterFill(LayoutAlignment verticalOptions, LayoutAlignment horizontalOptions) => verticalOptions == LayoutAlignment.Center && horizontalOptions == LayoutAlignment.Fill;
static bool IsBottomFill(LayoutAlignment verticalOptions, LayoutAlignment horizontalOptions) => verticalOptions == LayoutAlignment.End && horizontalOptions == LayoutAlignment.Fill;
static bool IsFill(LayoutAlignment verticalOptions, LayoutAlignment horizontalOptions) => verticalOptions == LayoutAlignment.Fill && horizontalOptions == LayoutAlignment.Fill;
}
}