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

Conversation

cat0363
Copy link
Contributor

@cat0363 cat0363 commented Aug 17, 2023

This PR fixes the following:

  1. The content of the Popup will not be displayed beyond the size specified in the Size property of the Popup.
  2. If Center is specified for VerticalOptions of Popup, the Popup will be displayed in the center of the Window where the Popup is displayed.
  3. If End is specified for VerticalOptions of Popup, the Popup will be displayed so that it fits at the bottom of the Window where the Popup is displayed.
  4. The position of the Popup will be updated according to the resizing of the Window that displays the Popup.

Description of Change

Modified as follows to update the position of the Popup following the resizing of the Window that displays the Popup.

[src\CommunityToolkit.Maui.Core\Handlers\Popup\PopUpHandler.windows.cs]

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

protected override void DisconnectHandler(Popup platformView)
{
    ArgumentNullException.ThrowIfNull(VirtualView.Parent);
    ArgumentNullException.ThrowIfNull(VirtualView.Handler?.MauiContext);
    var parent = VirtualView.Parent.ToPlatform(VirtualView.Handler.MauiContext);
    parent.IsHitTestVisible = true;
    platformView.IsOpen = false;
    platformView.Closed -= OnClosed;
    if (MauiContext is not null)
    {
        MauiContext.GetPlatformWindow().SizeChanged -= OnSizeChanged;
    }
}

void OnSizeChanged(object? sender, WindowSizeChangedEventArgs e)
{
    if (VirtualView is not null)
    {
        PopupExtensions.SetSize(PlatformView, VirtualView);
        PopupExtensions.SetLayout(PlatformView, VirtualView, MauiContext);
    }
}

Caught Window's SizeChanged event and called SetSize and SetLayout methods.
As a result, the position of the Popup is updated according to the resizing of the Window that displays the Popup.

Next, I added the following code to limit the Content Size to the Size specified in the Popup's Size property.

[src\CommunityToolkit.Maui.Core\Views\Popup\PopupExtensions.windows.cs]

public static void SetSize(this Popup mauiPopup, IPopup popup)
{
    omit ... 

    if (popup.Parent is IView popupParent)
    {
        currentSize.Width = Math.Min(currentSize.Width, popupParent.Frame.Width);
        currentSize.Height = Math.Min(currentSize.Height, popupParent.Frame.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;
    }
}

By explicitly specifying the size of the Popup's Child, the size of the Popup is now limited.

Next, I changed the code as follows so that it is displayed at the intended position when Center or End is specified for VerticalOptions of Popup.

static Rect? GetTitleBarRectangle(IMauiContext? mauiContext)
{
    if (!AppWindowTitleBar.IsCustomizationSupported())
    {
        return null;
    }

    if (mauiContext?.GetNavigationRootManager()?.RootView is WindowRootView rootView &&
        rootView.AppTitleBarContainer is FrameworkElement element)
    {
        return new Rect((int)element.Margin.Left, 0, (int)element.ActualWidth, (int)element.ActualHeight);
    }

    return null;
}

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

    Rect? titleBarRect = GetTitleBarRectangle(mauiContext);

    var verticalOptions = popup.VerticalOptions;
    var horizontalOptions = popup.HorizontalOptions;
    if (IsTopLeft(verticalOptions, horizontalOptions))
    {
        mauiPopup.DesiredPlacement = PopupPlacementMode.TopEdgeAlignedLeft;
        mauiPopup.HorizontalOffset = 0;
        mauiPopup.VerticalOffset = 0;
    }
    else if (IsTop(verticalOptions, horizontalOptions))
    {
        mauiPopup.DesiredPlacement = PopupPlacementMode.Top;
        mauiPopup.HorizontalOffset = (popupParentFrame.Width - contentSize.Width) / 2;
        mauiPopup.VerticalOffset = 0;
    }
    else if (IsTopRight(verticalOptions, horizontalOptions))
    {
        mauiPopup.DesiredPlacement = PopupPlacementMode.TopEdgeAlignedRight;
        mauiPopup.HorizontalOffset = (popupParentFrame.Width - contentSize.Width);
        mauiPopup.VerticalOffset = 0;
    }
    else if (IsRight(verticalOptions, horizontalOptions))
    {
        mauiPopup.DesiredPlacement = PopupPlacementMode.Right;
        mauiPopup.HorizontalOffset = (popupParentFrame.Width - contentSize.Width);
        mauiPopup.VerticalOffset = (popupParentFrame.Height - contentSize.Height / 2 + titleBarRect?.Height ?? 0) / 2;
    }
    else if (IsBottomRight(verticalOptions, horizontalOptions))
    {
        mauiPopup.DesiredPlacement = PopupPlacementMode.BottomEdgeAlignedRight;
        mauiPopup.HorizontalOffset = (popupParentFrame.Width - contentSize.Width);
        mauiPopup.VerticalOffset = popupParentFrame.Height - contentSize.Height / 2 + titleBarRect?.Height ?? 0;
    }
    else if (IsBottom(verticalOptions, horizontalOptions))
    {
        mauiPopup.DesiredPlacement = PopupPlacementMode.Bottom;
        mauiPopup.HorizontalOffset = (popupParentFrame.Width - contentSize.Width) / 2;
        mauiPopup.VerticalOffset = popupParentFrame.Height - contentSize.Height / 2 + titleBarRect?.Height ?? 0;
    }
    else if (IsBottomLeft(verticalOptions, horizontalOptions))
    {
        mauiPopup.DesiredPlacement = PopupPlacementMode.BottomEdgeAlignedLeft;
        mauiPopup.HorizontalOffset = 0;
        mauiPopup.VerticalOffset = popupParentFrame.Height - contentSize.Height / 2 + titleBarRect?.Height ?? 0;
    }
    else if (IsLeft(verticalOptions, horizontalOptions))
    {
        mauiPopup.DesiredPlacement = PopupPlacementMode.Left;
        mauiPopup.HorizontalOffset = 0;
        mauiPopup.VerticalOffset = (popupParentFrame.Height - contentSize.Height / 2 + titleBarRect?.Height ?? 0) / 2;
    }
    else if (popup.Anchor is null)
    {
        mauiPopup.DesiredPlacement = PopupPlacementMode.Auto;
        mauiPopup.HorizontalOffset = (popupParentFrame.Width - contentSize.Width) / 2;
        mauiPopup.VerticalOffset = (popupParentFrame.Height - contentSize.Height / 2 + titleBarRect?.Height ?? 0) / 2;
    }
    else
    {
        mauiPopup.DesiredPlacement = PopupPlacementMode.Top;
    }
    
    omit ...
}

I had to get the height of the Window's title bar because it wasn't taking into account the height of the Window's title bar.
The GetTitleBarRectangle method that acquires the Rectangle of the title bar added this time was implemented with reference to the following source code of .NET MAUI.

[src\Platform\Windows\WindowExtensions.cs]

internal static Rect[]? GetDefaultTitleBarDragRectangles(this UI.Xaml.Window platformWindow, IMauiContext mauiContext)
{
    if (!AppWindowTitleBar.IsCustomizationSupported())
        return null;

    if (mauiContext?.GetNavigationRootManager()?.RootView is WindowRootView rootView &&
        rootView.AppTitleBarContainer is FrameworkElement element)
    {
        return new[]
        {
            new Rect((int)element.Margin.Left, 0, (int)element.ActualWidth, (int)element.ActualHeight),
        };
    }

    return null;
}

With this fix, when Center or End is specified for VerticalOptions of Popup, it is now displayed at the intended position.

Linked Issues

PR Checklist

Additional information

The following is the verification result when the Content Size exceeds the Size specified in the Popup Size property.

Size = "100, 100" HorizontalOptions = "Start" VerticalOptions = "Center"

verify_001

You can see that the Popup's Content Size is limited by the Size specified in the Popup's Size property.

Next is the verification result when Center is specified for VerticalOptions of Popup.

Size = "100, 100" HorizontalOptions = "Start" VerticalOptions = "Center"

verify_002

Size = "100, 100" HorizontalOptions = "Center" VerticalOptions = "Center"

verify_003

Size = "100, 100" HorizontalOptions = "End" VerticalOptions = "Center"

verify_004

The red line in the execution result is the horizontal and vertical center.
You can see that the Popup is displayed in the center of the Window from which the Popup is displayed when Center is specified for the VerticalOptions of the Popup.

Next is the verification result when End is specified for VerticalOptions of Popup.

Size = "100, 100" HorizontalOptions = "Start" VerticalOptions = "End"

verify_005

Size = "100, 100" HorizontalOptions = "Center" VerticalOptions = "End"

verify_006

Size = "100, 100" HorizontalOptions = "End" VerticalOptions = "End"

verify_007

You can see that when the Popup's VerticalOptions is set to End, the Popup is displayed so that it fits at the bottom of the Window from which the Popup is displayed.

Next is the verification result when resizing the Window that is the display source of the Popup.

Size = "100, 100" HorizontalOptions = "Start" VerticalOptions = "End" CanBeDismissedByTappingOutsideOfPopup = "False"

[Before Resize] [After Resize]

You can see that the position of the Popup is updated according to the resizing of the Window that displays the Popup.

Concerns:
I can't verify it because I don't have a verification environment for Windows 10 at hand.

@cat0363
Copy link
Contributor Author

cat0363 commented Aug 17, 2023

Additional Information:
Below is information about the support status of the AppWindowTitleBar.IsCustomizationSupported method.
https://learn.microsoft.com/en-us/windows/apps/develop/title-bar?tabs=wasdk

@cat0363 cat0363 mentioned this pull request Aug 17, 2023
6 tasks
@cat0363
Copy link
Contributor Author

cat0363 commented Aug 17, 2023

The PR I uploaded today does not support maximizing/minimizing the window that displays the Popup, so we will fix it.
Please wait a moment now.

@cat0363
Copy link
Contributor Author

cat0363 commented Aug 17, 2023

In order to support maximizing/minimizing the window, the acquisition of the window
from which the Popup is displayed has been changed as follows.

var popupParent = mauiContext.GetPlatformWindow();

This removed the need to get the title bar height.

In addition, the calculation formulas for horizontal position and vertical position have been changed.
In addition, added behavior when Fill is specified for VerticalOptions and HorizontalOptions of Popup.

Copy link
Collaborator

@VladislavAntonyuk VladislavAntonyuk left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Could you please resolve conflicts?

@cat0363
Copy link
Contributor Author

cat0363 commented Aug 24, 2023

@VladislavAntonyuk , I resolved the conflict.

@brminnick brminnick merged commit 22b158a into CommunityToolkit:main Sep 7, 2023
8 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

[BUG] Incorrect Position and Size of Popup on Windows
4 participants