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 an issue with Popup size and position on Android #1683

Merged
merged 11 commits into from Mar 25, 2024

Conversation

cat0363
Copy link
Contributor

@cat0363 cat0363 commented Feb 8, 2024

This PR will solve the following problems.

  1. An issue where the Popup content is not laid out correctly when the Popup size is not specified.
  2. An Issue where Popup position shifts when an anchor is specified for Popup.

Description of Change

The solution to the first problem is shown below.

I made a mistake in PR #1520 by calling the Measure method on all child elements of the Popup's Content if you do not explicitly specify the Popup's size. There was also a mistake in the way PR #1456 addressed the label wrapping issue.
Calling the Measure method carelessly can produce unintended results in Android.

I modified it to call the Measure method only if the Popup size is not explicitly specified.
I also modified it to not call the Measure method of the Popup's Content if there is no change in the Popup's size or screen orientation.

If the Popup size is not explicitly specified, LayoutOptions is other than Fill, and the result of calling the Measure method does not exceed the screen size, the argument of the SetLayout method remains WrapContent, so in that case, MeasuredWidth, MeasuredHeight should be set. It will be retained as the previous value.

Therefore, I have made changes to retain the previous values ​​of Popup and screen size.

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

internal int LastPopupWidth { get; set; }
internal int LastPopupHeight { get; set; }
internal double LastWindowWidth { get; set; }
internal double LastWindowHeight { get; set; }

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

public static void SetSize(this Dialog dialog, in IPopup popup, in AView container, PopupHandler handler)
{
    ArgumentNullException.ThrowIfNull(dialog);
    ArgumentNullException.ThrowIfNull(container);
    ArgumentNullException.ThrowIfNull(popup?.Content);
    ArgumentNullException.ThrowIfNull(handler);

    var window = GetWindow(dialog);
    var context = dialog.Context;
    var windowManager = window.WindowManager;

    var decorView = (ViewGroup)window.DecorView;
    var child = decorView.GetChildAt(0) ?? throw new InvalidOperationException($"No child found in {nameof(ViewGroup)}");

    var windowSize = GetWindowSize(windowManager);
    int width = LayoutParams.WrapContent;
    int height = LayoutParams.WrapContent;

    if (popup.Size.IsZero)
    {
        if (double.IsNaN(popup.Content.Width) || double.IsNaN(popup.Content.Height))
        {
            if ((handler.LastPopupWidth == decorView.MeasuredWidth &&
                 handler.LastPopupHeight == decorView.MeasuredHeight) &&
                (handler.LastWindowWidth == windowSize.Width &&
                 handler.LastWindowHeight == windowSize.Height))
            {
                SetAnchor(dialog, popup, handler.LastPopupWidth, handler.LastPopupHeight);
                return;
            }

            decorView.Measure(
                MeasureSpecExtensions.MakeMeasureSpec(MeasureSpecMode.AtMost, (int)windowSize.Width),
                MeasureSpecExtensions.MakeMeasureSpec(MeasureSpecMode.AtMost, (int)windowSize.Height)
            );

            if (double.IsNaN(popup.Content.Width))
            {
                if (popup.HorizontalOptions == LayoutAlignment.Fill)
                {
                    width = (int)windowSize.Width;
                }
                else
                {
                    if (decorView.MeasuredWidth >= windowSize.Width)
                    {
                        width = (int)windowSize.Width;
                    }
                }
            }
            else
            {
                if (context.ToPixels(popup.Content.Width) >= windowSize.Width)
                {
                    width = (int)windowSize.Width;
                }
                else
                {
                    width = (int)context.ToPixels(popup.Content.Width);
                }
            }

            if (double.IsNaN(popup.Content.Height))
            {
                if (popup.VerticalOptions == LayoutAlignment.Fill)
                {
                    height = (int)windowSize.Height;
                }
                else
                {
                    if (decorView.MeasuredHeight >= windowSize.Height)
                    {
                        height = (int)windowSize.Height;
                    }
                }
            }
            else
            {
                if (context.ToPixels(popup.Content.Height) >= windowSize.Height)
                {
                    height = (int)windowSize.Height;
                }
                else
                {
                    height = (int)context.ToPixels(popup.Content.Height);
                }
            }

            window.SetLayout(width, height);

            width = width == LayoutParams.WrapContent ? decorView.MeasuredWidth : width;
            height = height == LayoutParams.WrapContent ? decorView.MeasuredHeight : height;

            handler.LastPopupWidth = decorView.Width;
            handler.LastPopupHeight = decorView.Height;
            handler.LastWindowWidth = windowSize.Width;
            handler.LastWindowHeight = windowSize.Height;
        }
        else
        {
            width = (int)context.ToPixels(popup.Content.Width);
            height = (int)context.ToPixels(popup.Content.Height);
            width = width > windowSize.Width ? (int)windowSize.Width : width;
            height = height > windowSize.Height ? (int)windowSize.Height : height;

            if ((handler.LastPopupWidth == width &&
                 handler.LastPopupHeight == height) &&
                (handler.LastWindowWidth == windowSize.Width &&
                 handler.LastWindowHeight == windowSize.Height))
            {
                SetAnchor(dialog, popup, handler.LastPopupWidth, handler.LastPopupHeight);
                return;
            }

            window.SetLayout(width, height);

            handler.LastPopupWidth = decorView.Width;
            handler.LastPopupHeight = decorView.Height;
            handler.LastWindowWidth = windowSize.Width;
            handler.LastWindowHeight = windowSize.Height;
        }
    }
    else
    {
        width = (int)context.ToPixels(popup.Size.Width);
        height = (int)context.ToPixels(popup.Size.Height);
        width = width > windowSize.Width ? (int)windowSize.Width : width;
        height = height > windowSize.Height ? (int)windowSize.Height : height;

        if ((handler.LastPopupWidth == width &&
             handler.LastPopupHeight == height) &&
            (handler.LastWindowWidth == windowSize.Width &&
             handler.LastWindowHeight == windowSize.Height))
        {
            SetAnchor(dialog, popup, handler.LastPopupWidth, handler.LastPopupHeight);
            return;
        }

        window.SetLayout(width, height);

        handler.LastPopupWidth = decorView.Width;
        handler.LastPopupHeight = decorView.Height;
        handler.LastWindowWidth = windowSize.Width;
        handler.LastWindowHeight = windowSize.Height;
    }

    SetAnchor(dialog, popup, width, height);
}

The solution to the second problem is shown below.

In the source code before modification, when Anchor is specified, the Popup is displayed near the center of Anchor, but it is not exactly in the center.
This is because the height of the StatusBar and NavigationBar is not taken into account when calculating the popup display position.
Therefore, we have modified it to take into account the height of the StatusBar and NavigationBar when calculating the popup display position.

This time, I have newly added GetStatusBarHeight and GetNavigationBarHeight methods.
Additionally, I changed the definition location of the GetWindowSize method.

If the Popup size is explicitly specified, the Popup content size is specified, or the Popup size is obtained by the Measure method, I used these to calculate the Popup display position.

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

public static void SetAnchor(this Dialog dialog, in IPopup popup, int? popupWidth = null, int? popupHeight = null)
{
    var window = GetWindow(dialog);

    var windowManager = window.WindowManager;
    var statusBarHeight = GetStatusBarHeight(windowManager);
    var navigationBarHeight = GetNavigationBarHeight(windowManager);
    var windowSize = GetWindowSize(windowManager);
    var rotation = windowManager!.DefaultDisplay!.Rotation;
    navigationBarHeight = windowSize.Height < windowSize.Width ? (rotation == SurfaceOrientation.Rotation270 ? navigationBarHeight : 0) : 0;

    if (popup.Handler?.MauiContext is null)
    {
        return;
    }

    if (popup.Anchor is not null)
    {
        var anchorView = popup.Anchor.ToPlatform();

        var locationOnScreen = new int[2];
        anchorView.GetLocationOnScreen(locationOnScreen);
        if (popupWidth is null && popupHeight is null)
        {
            window.DecorView.Measure((int)MeasureSpecMode.Unspecified, (int)MeasureSpecMode.Unspecified);
        }

        // This logic is tricky, please read these notes if you need to modify
        // Android window coordinate starts (0,0) at the top left and (max,max) at the bottom right. All of the positions
        // that are being handled in this operation assume the point is at the top left of the rectangle. This means the
        // calculation operates in this order:
        // 1. Calculate top-left position of Anchor
        // 2. Calculate the Actual Center of the Anchor by adding the width /2 and height / 2
        // 3. Calculate the top-left point of where the dialog should be positioned by subtracting the Width / 2 and height / 2
        //    of the dialog that is about to be drawn.
        var attribute = window.Attributes ?? throw new InvalidOperationException($"{nameof(window.Attributes)} cannot be null");

        var newX = locationOnScreen[0] - navigationBarHeight + (anchorView.Width / 2) - (popupWidth == null ? (window.DecorView.Width / 2) : (int)(popupWidth / 2));
        var newY = locationOnScreen[1] - statusBarHeight + (anchorView.Height / 2) - (popupHeight == null ? (window.DecorView.Height / 2) : (int)(popupHeight / 2));
		
        if (!(newX == attribute.X &&
              newY == attribute.Y))
        {
            window.SetGravity(GravityFlags.Top | GravityFlags.Left);
            attribute.X = newX;
            attribute.Y = newY;
            window.Attributes = attribute;
        }
    }
    else
    {
        SetDialogPosition(popup, window);
    }
}

static int GetStatusBarHeight(IWindowManager? windowManager)
{
    ArgumentNullException.ThrowIfNull(windowManager);

    int statusBarHeight;

    if (OperatingSystem.IsAndroidVersionAtLeast((int)BuildVersionCodes.R))
    {
        var windowMetrics = windowManager.CurrentWindowMetrics;
        var windowInsets = windowMetrics.WindowInsets.GetInsetsIgnoringVisibility(WindowInsets.Type.SystemBars());
        statusBarHeight = windowInsets.Top;
    }
    else if (windowManager.DefaultDisplay is null)
    {
        throw new InvalidOperationException($"{nameof(IWindowManager)}.{nameof(IWindowManager.DefaultDisplay)} cannot be null");
    }
    else
    {
        APoint realSize = new();
        APoint displaySize = new();
        APoint displaySmallSize = new();
        APoint displayLargeSize = new();

        windowManager.DefaultDisplay.GetRealSize(realSize);
        ArgumentNullException.ThrowIfNull(realSize);

        windowManager.DefaultDisplay.GetSize(displaySize);
        ArgumentNullException.ThrowIfNull(displaySize);

        windowManager.DefaultDisplay.GetCurrentSizeRange(displaySmallSize, displayLargeSize);
        ArgumentNullException.ThrowIfNull(displaySmallSize);
        ArgumentNullException.ThrowIfNull(displayLargeSize);

        if (displaySize.X > displaySize.Y)
        {
            statusBarHeight = displaySize.Y - displaySmallSize.Y;
        }
        else
        {
            statusBarHeight = displaySize.Y - displayLargeSize.Y;
        }
    }

    return statusBarHeight;
}

static int GetNavigationBarHeight(IWindowManager? windowManager)
{
    ArgumentNullException.ThrowIfNull(windowManager);

    int windowWidth;
    int windowHeight;
    int navigationBarHeight;

    if (OperatingSystem.IsAndroidVersionAtLeast((int)BuildVersionCodes.R))
    {
        var windowMetrics = windowManager.CurrentWindowMetrics;
        var windowInsets = windowMetrics.WindowInsets.GetInsetsIgnoringVisibility(WindowInsets.Type.SystemBars());
        windowWidth = windowMetrics.Bounds.Width();
        windowHeight = windowMetrics.Bounds.Height();
        navigationBarHeight = windowHeight < windowWidth ? windowInsets.Left + windowInsets.Right : windowInsets.Bottom;
    }
    else if (windowManager.DefaultDisplay is null)
    {
        throw new InvalidOperationException($"{nameof(IWindowManager)}.{nameof(IWindowManager.DefaultDisplay)} cannot be null");
    }
    else
    {
        APoint realSize = new();
        APoint displaySize = new();
        APoint displaySmallSize = new();
        APoint displayLargeSize = new();

        windowManager.DefaultDisplay.GetRealSize(realSize);
        ArgumentNullException.ThrowIfNull(realSize);

        windowManager.DefaultDisplay.GetSize(displaySize);
        ArgumentNullException.ThrowIfNull(displaySize);

        windowManager.DefaultDisplay.GetCurrentSizeRange(displaySmallSize, displayLargeSize);
        ArgumentNullException.ThrowIfNull(displaySmallSize);
        ArgumentNullException.ThrowIfNull(displayLargeSize);

        windowWidth = realSize.X;
        windowHeight = realSize.Y;

        navigationBarHeight = realSize.Y < realSize.X
            ? (realSize.X - displaySize.X)
            : (realSize.Y - displaySize.Y);
    }

    return navigationBarHeight;
}

Linked Issues

PR Checklist

  • Has a linked Issue, and the Issue has been approved(bug) or Championed (feature/proposal)
  • Has tests (if omitted, state reason in description)
  • Has samples (if omitted, state reason in description)
  • Rebased on top of main at time of PR
  • Changes adhere to coding standard
  • Documentation created or updated: https://github.com/MicrosoftDocs/CommunityToolkit/pulls

Additional information

For verification, I used the following Nightly Build of .NET MAUI.

8.0.7-nightly.*

Below are the verification results.

[No.1]

<toolkit:Popup xmlns="http://schemas.microsoft.com/dotnet/2021/maui"
    xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
    xmlns:toolkit="http://schemas.microsoft.com/dotnet/2022/maui/toolkit"
    x:Class="MauiComm_VerifyPopupAndroid.PopupPage1"
    Color="Transparent" Size="300,300" HorizontalOptions="Center" VerticalOptions="Center">
    <Grid>
        <Label Text="Hello, World!" FontSize="32" BackgroundColor="LightBlue" VerticalTextAlignment="Center" HorizontalTextAlignment="Center" />
        <Button Text="Click me" VerticalOptions="Start" HorizontalOptions="Center" />
    </Grid>
</toolkit:Popup>

[No.2]

<toolkit:Popup xmlns="http://schemas.microsoft.com/dotnet/2021/maui"
    xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
    xmlns:toolkit="http://schemas.microsoft.com/dotnet/2022/maui/toolkit"
    x:Class="MauiComm_VerifyPopupAndroid.PopupPage2" 
    Size="100,100" HorizontalOptions="Start" VerticalOptions="Center">
    <Grid>
        <Grid.GestureRecognizers>
            <TapGestureRecognizer  Tapped="gd_Tapped" />
        </Grid.GestureRecognizers>
        <Label Text="1234567890ABCDEFGHIJKLMNOPQRSTUVWXYZ" TextColor="Black" HorizontalOptions="Start" />
    </Grid>
</toolkit:Popup>

[No.3]

<toolkit:Popup xmlns="http://schemas.microsoft.com/dotnet/2021/maui"
    xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
    xmlns:toolkit="http://schemas.microsoft.com/dotnet/2022/maui/toolkit"
    x:Class="MauiComm_VerifyPopupAndroid.PopupPage3" 
    Size="100,100" HorizontalOptions="End" VerticalOptions="End">
    <Grid>
        <Grid WidthRequest="10" HeightRequest="10" VerticalOptions="Start" HorizontalOptions="Start" BackgroundColor="Blue" />
        <Grid WidthRequest="10" HeightRequest="10" VerticalOptions="Start" HorizontalOptions="End" BackgroundColor="Blue" />
        <Grid WidthRequest="10" HeightRequest="10" VerticalOptions="End" HorizontalOptions="Start" BackgroundColor="Blue" />
        <Grid WidthRequest="10" HeightRequest="10" VerticalOptions="End" HorizontalOptions="End" BackgroundColor="Blue" />
    </Grid>
</toolkit:Popup>

[No.4] (Test to dynamically change the label in Popup's Content)

<toolkit:Popup xmlns="http://schemas.microsoft.com/dotnet/2021/maui"
    xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
    xmlns:toolkit="http://schemas.microsoft.com/dotnet/2022/maui/toolkit"
    x:Class="MauiComm_VerifyPopupAndroid.PopupPage4"
    HorizontalOptions="Center" VerticalOptions="Center">
    <Grid>
        <Grid ColumnDefinitions="*" RowDefinitions="auto,auto,auto" >
            <Label x:Name="testlabel1" BackgroundColor="Red" HorizontalTextAlignment="Center"/>
            <Label x:Name="testlabel2" Grid.Row="1" />
            <VerticalStackLayout Grid.Row="2" Spacing="4">
                <Label x:Name="testlabel3" BackgroundColor="Red" HorizontalTextAlignment="Center"/>
                <Label x:Name="testlabel4" />
            </VerticalStackLayout>
        </Grid>
        <Grid WidthRequest="10" HeightRequest="10" BackgroundColor="Blue" VerticalOptions="Start" HorizontalOptions="Start" />
        <Grid WidthRequest="10" HeightRequest="10" BackgroundColor="Blue" VerticalOptions="Start" HorizontalOptions="End" />
        <Grid WidthRequest="10" HeightRequest="10" BackgroundColor="Blue" VerticalOptions="End" HorizontalOptions="Start" />
        <Grid WidthRequest="10" HeightRequest="10" BackgroundColor="Blue" VerticalOptions="End" HorizontalOptions="End" />
    </Grid>
</toolkit:Popup>

[No.5] (Test to set a long string to the label text in Popup's Content)

<toolkit:Popup xmlns="http://schemas.microsoft.com/dotnet/2021/maui"
    xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
    xmlns:toolkit="http://schemas.microsoft.com/dotnet/2022/maui/toolkit"
    x:Class="MauiComm_VerifyPopupAndroid.PopupPage5"
    HorizontalOptions="Center" VerticalOptions="Center">
    <VerticalStackLayout>
        <Label Text="Popup" FontSize="20"/>
        <BoxView HeightRequest="3" Color="AliceBlue" />
        <Label x:Name="txt1" Text="Short String" />
        <Label x:Name="txt2" Text="Long String1 Long String2 Long String3 Long String4 Long String5 Long String6 Long String7 Long String8 Long String9 Long String10 Long String" />
    </VerticalStackLayout>
</toolkit:Popup>

[No.6] Test for rounding popup corners

<toolkit:Popup xmlns="http://schemas.microsoft.com/dotnet/2021/maui"
    xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
    xmlns:toolkit="http://schemas.microsoft.com/dotnet/2022/maui/toolkit"
    x:Class="MauiComm_VerifyPopupAndroid.PopupPage6" 
    Size="200,200" HorizontalOptions="Center" VerticalOptions="Center" Color="Transparent">
    <Border StrokeShape="RoundRectangle 5,5,5,5" BackgroundColor="Red" StrokeThickness="0">        
        <Grid BackgroundColor="Red">
            <Grid WidthRequest="10" HeightRequest="10" VerticalOptions="Start" HorizontalOptions="Start" BackgroundColor="Blue" />
            <Grid WidthRequest="10" HeightRequest="10" VerticalOptions="Start" HorizontalOptions="End" BackgroundColor="Blue" />
            <Grid WidthRequest="10" HeightRequest="10" VerticalOptions="End" HorizontalOptions="Start" BackgroundColor="Blue" />
            <Grid WidthRequest="10" HeightRequest="10" VerticalOptions="End" HorizontalOptions="End" BackgroundColor="Blue" />
        </Grid>
    </Border>
</toolkit:Popup>

[No.7]

<toolkit:Popup xmlns="http://schemas.microsoft.com/dotnet/2021/maui"
    xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
    xmlns:toolkit="http://schemas.microsoft.com/dotnet/2022/maui/toolkit"
    x:Class="MauiComm_VerifyPopupAndroid.PopupPage7"
    HorizontalOptions="Start" VerticalOptions="Start">
    <Grid>
        <ScrollView>
            <StackLayout>
                <Image Source="dotnet_bot.png" HeightRequest="200" />
                <Label Text="Hello, World!" HorizontalTextAlignment="Center" />
            </StackLayout>
        </ScrollView>
    </Grid>
</toolkit:Popup>

The result is similar to the .NET MAUI side. This behavior cannot be changed. An explicit size must be specified.

[No.8] (This is a test for Issue #1489.)

<toolkit:Popup xmlns="http://schemas.microsoft.com/dotnet/2021/maui"
    xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
    xmlns:toolkit="http://schemas.microsoft.com/dotnet/2022/maui/toolkit"
    x:Class="MauiComm_VerifyPopupAndroid.PopupPage8"
    HorizontalOptions="Center" VerticalOptions="Center">
    <VerticalStackLayout>
        <Label x:Name="PopUp" Text="     Critical Alert Testing     " BackgroundColor="White" TextColor="Black" FontSize="Medium" VerticalOptions="Center" HorizontalOptions="Center" />
        <Button x:Name="Button_Continue" Text=" Continue " BackgroundColor="Red" TextColor="White" FontSize="Large" />
    </VerticalStackLayout>
</toolkit:Popup>
Android.Emulator.-.pixel_2_-_api_30_5554.2024-02-08.13-37-08.mp4

Below is the execution result after rotating the device.

[No.9] (Testing VerticalStackLayout.)

<toolkit:Popup xmlns="http://schemas.microsoft.com/dotnet/2021/maui"
    xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
    xmlns:toolkit="http://schemas.microsoft.com/dotnet/2022/maui/toolkit"
    x:Class="MauiComm_VerifyPopupAndroid.PopupPage9"
    HorizontalOptions="Center" VerticalOptions="Center">
    <VerticalStackLayout>
        <Label x:Name="PopUp" Text="     Critical Alert Testing     " BackgroundColor="White" TextColor="Black" FontSize="Medium" VerticalOptions="Center" HorizontalOptions="Center" />
        <Button Text="ABC" BackgroundColor="Red" TextColor="White" FontSize="Large" HorizontalOptions="Start" />
        <Button Text="CDE" BackgroundColor="Red" TextColor="White" FontSize="Large" HorizontalOptions="Center" />
        <Button Text="EDF" BackgroundColor="Red" TextColor="White" FontSize="Large" HorizontalOptions="End" />
        <Button Text="HIJ" BackgroundColor="Red" TextColor="White" FontSize="Large" HorizontalOptions="Fill" />
    </VerticalStackLayout>
</toolkit:Popup>

[No.10] (Testing HorizontalStackLayout.)

<toolkit:Popup xmlns="http://schemas.microsoft.com/dotnet/2021/maui"
    xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
    xmlns:toolkit="http://schemas.microsoft.com/dotnet/2022/maui/toolkit"
    x:Class="MauiComm_VerifyPopupAndroid.PopupPage10"
    HorizontalOptions="End" VerticalOptions="Center">
    <VerticalStackLayout>
        <Label x:Name="PopUp" Text="     Critical Alert Testing     " BackgroundColor="White" TextColor="Black" FontSize="Medium" VerticalOptions="Center" HorizontalOptions="Center" />
        <HorizontalStackLayout HeightRequest="132">
            <Button Text="ABC" BackgroundColor="Red" TextColor="White" FontSize="Large" VerticalOptions="Start" WidthRequest="100" />
            <Button Text="CDE" BackgroundColor="Red" TextColor="White" FontSize="Large" VerticalOptions="Center" />
            <Button Text="EDF" BackgroundColor="Red" TextColor="White" FontSize="Large" VerticalOptions="End" />
            <Button Text="HIJ" BackgroundColor="Red" TextColor="White" FontSize="Large" VerticalOptions="Fill" />
        </HorizontalStackLayout>
    </VerticalStackLayout>
</toolkit:Popup>

[No.11] (Testing Grid.)

<toolkit:Popup xmlns="http://schemas.microsoft.com/dotnet/2021/maui"
    xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
    xmlns:toolkit="http://schemas.microsoft.com/dotnet/2022/maui/toolkit"
    x:Class="MauiComm_VerifyPopupAndroid.PopupPage11"
    Size="300,300" HorizontalOptions="End" VerticalOptions="End">
    <Grid RowDefinitions="Auto,*,Auto" ColumnDefinitions="100,*" Padding="10">
        <Button Grid.Row="0" Grid.Column="0" Text="ABC" BackgroundColor="Red" />
        <Button Grid.Row="0" Grid.Column="1" Text="CDE" HorizontalOptions="Start" VerticalOptions="Start" />
        <Button Grid.Row="1" Grid.Column="0" Text="EFG" />
        <Button Grid.Row="1" Grid.Column="1" Text="HIJ" HorizontalOptions="End" VerticalOptions="End" />
        <Button Grid.Row="2" Grid.Column="0" Text="KLM" BackgroundColor="Red" />
        <Border Grid.Row="2" Grid.Column="1" StrokeThickness="1" Stroke="Blue" BackgroundColor="Yellow" WidthRequest="48" HeightRequest="48" StrokeShape="RoundRectangle, 24,24,24,24">
            <Label Text="AA" TextColor="Black" VerticalOptions="Center" HorizontalOptions="Center" />
        </Border>
    </Grid>
</toolkit:Popup>

[No.12] (Issue #1603)

<toolkit:Popup xmlns="http://schemas.microsoft.com/dotnet/2021/maui"
    xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
    xmlns:toolkit="http://schemas.microsoft.com/dotnet/2022/maui/toolkit"
    x:Class="MauiComm_VerifyPopupAndroid.PopupPage12"
    HorizontalOptions="Start" VerticalOptions="Start">
    <ScrollView VerticalOptions="Center" BackgroundColor="Transparent">
        <Frame VerticalOptions="Start" CornerRadius="5" IsClippedToBounds="True" HasShadow="False" BackgroundColor="White" Margin="20" Padding="10">
            <StackLayout>
                <Label Text="Long Text Long Text Long Text Long Text Long Text Long Text Long Text Long Text 
                   Long Text Long Text Long Text Long Text Long Text Long Text Long Text Long Text Long 
                   Text Long Text Long Text Long Text Long Text Long Text Long Text Long Text Long Text 
                   Long Text Long Text Long Text Long Text Long Text Long Text Long Text Long Text Long Text " 
                   FontSize="Medium" />
                <Editor />
            </StackLayout>
        </Frame>
    </ScrollView>
</toolkit:Popup>
Android.Emulator.-.pixel_2_-_api_30_5554.2024-02-08.10-58-14.mp4

You can see that the Popup is displayed as intended in both layouts.

Below are the verification results for each issue.

[Issue #1532]

Android.Emulator.-.pixel_2_-_api_30_5554.2024-02-08.11-30-03.mp4

[Issue #1575]

Android.Emulator.-.pixel_2_-_api_30_5554.2024-02-08.12-28-41.mp4

[Issue #1489]

Android.Emulator.-.pixel_2_-_api_30_5554.2024-02-08.12-45-24.mp4

[Issue #1592]

Android.Emulator.-.pixel_2_-_api_30_5554.2024-02-08.12-54-45.mp4

You can see that each issue has been resolved by this PR.

Finally, the verification results when specifying Anchor for Popup are shown below.
For verification, we used a modified sample program.
I used Border because it was difficult to see the exact center of the anchor with the cross-shaped text on the label.

[Anchor Layout]

<Border x:Name="Indicator" StrokeShape="RoundRectangle 25,25,25,25" WidthRequest="50" HeightRequest="50" BackgroundColor="Blue" Stroke="Black">
    <Border.GestureRecognizers>
        <PanGestureRecognizer PanUpdated="OnPanUpdated" />
    </Border.GestureRecognizers>
</Border>

[Popup Layout]

<toolkit:Popup xmlns="http://schemas.microsoft.com/dotnet/2021/maui"
    xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
    xmlns:toolkit="http://schemas.microsoft.com/dotnet/2022/maui/toolkit"
    x:Class="CommunityToolkit.Maui.Sample.Views.Popups.TransparentPopupCSharp"
    Color="Transparent">
    <Grid Margin="0" Padding="0" BackgroundColor="Transparent">
        <Border StrokeShape="RoundRectangle 25,25,25,25" BackgroundColor="Red" Stroke="Black" WidthRequest="30" HeightRequest="30" />
    </Grid>
</toolkit:Popup>

You can see that a red circle Popup is displayed in the center of the blue circle anchor.

Android.Emulator.-.pixel_2_-_api_30_5554.2024-02-08.13-11-11.mp4

Below are the verification results before and after screen rotation.

[Before Rotation] [After Rotation]

When refactoring, it is best to do so with caution. The impact of the changes is huge.
I'm sorry, but I can't spend any more time on this issue.

Even with this PR, Issue #1585 is not resolved. There is a high possibility that this is a problem on the MAUI side.

@brminnick brminnick added the breaking change This label is used for PRs that include a breaking change label Feb 17, 2024
@bijington
Copy link
Contributor

I have tested this with our samples and they all appear to behave as expected. Do we need to add more samples to cover the issues this is solving? I am happy to get involved and add those.

@cat0363
Copy link
Contributor Author

cat0363 commented Feb 27, 2024

@bijington , I agree with your opinion. Having sample code makes it easier to verify operation.

@imsam67
Copy link

imsam67 commented Feb 27, 2024

I just tested it using CommunityToolkit.Maui 99.0.0-preview1491 and the alignment issues seem to be fixed but I'm experiencing another issue with Label controls inside a Popup. See my post here #1621 (comment)

@cat0363
Copy link
Contributor Author

cat0363 commented Mar 15, 2024

@imsam67 , Am I correct in understanding that you tested the Preview version of PR #1520, not the Preview version of PR #1683?

@imsam67
Copy link

imsam67 commented Mar 15, 2024

@cat0363 I've been testing it with all the previews for the last few weeks -- the latest I tested it with is 99.0.0-preview1547. I'm not sure if PR 1520 and 1683 have been merged into 99.0.0-preview1547.

As I mentioned in my previous comments, the size and positioning issues seem to have been fixed now but there's another issue -- and I think related one -- still remains -- see it at #1621 (comment)

Originally, I thought label controls didn't display text when placed inside a popup but if you see the screen capture I did, you'll see that once I scroll up and down, text starts to display. I'm not sure if this issue is related to this one here but I think it may be.

@cat0363
Copy link
Contributor Author

cat0363 commented Mar 15, 2024

@imsam67 , Thank you for your reply.
The new issue you reported is similar to Issue #1532.
Since I don't have the repro code, I can't determine if the issue you reported is the same issue as #1532 or a different issue.

I don't know if this PR is included in the preview you listed, but can I obtain the preview package for this PR by following the steps below?

https://github.com/CommunityToolkit/Maui/wiki/Preview-Packages

@Kas-code
Copy link

Hi @cat0363 I have tried to follow the steps to get the preview package, when I click on the details link shown below, to go to the Azure Pipeline where the PR was built I get: "401 - Uh-oh, you do not have access"
image

If you can provide me with a .nupkg file I will test and add my results here, maybe attach the .nupkg file to this thread would be easiest?

@imsam67
Copy link

imsam67 commented Mar 15, 2024

@cat0363 The issue I'm experiencing is indeed the same issue as reported in #1532

I also went through the process of installing the package generated for #1683 and it certainly fixes the issue!

How can we get your fix into, at least, the latest preview package? I really appreciate your tackling these issues that seem somewhat unpopular. Thank you for your efforts :-)

@maonaoda
Copy link

when i removed this code:

Label in Popup.Border(with padding).Grid can finally handle the center HorizontalTextAlignment .

if (view is AppCompatTextView)
{
// https://github.com/dotnet/maui/issues/2019
// https://github.com/dotnet/maui/pull/2059
var layoutParams = view.LayoutParameters;
view.Measure(
MeasureSpec.MakeMeasureSpec(width, (layoutParams?.Width == LinearLayout.LayoutParams.WrapContent && !isNanWidth) ? MeasureSpecMode.Exactly : MeasureSpecMode.Unspecified),
MeasureSpec.MakeMeasureSpec(height, (layoutParams?.Height == LinearLayout.LayoutParams.WrapContent && !isNanHeight) ? MeasureSpecMode.Exactly : MeasureSpecMode.Unspecified)
);
}

@brminnick
Copy link
Collaborator

Just following up - since this has a breaking change to the API, and we're supper close to merging TouchBehavior, I'll be merging this PR after we merge TouchBehavior and then we'll publish v8.0.0 of CommunityToolkit.Maui 👍

@maonaoda
Copy link

happy to hear that, it's been so long since the last release.

Just following up - since this has a breaking change to the API, and we're supper close to merging TouchBehavior, I'll be merging this PR after we merge TouchBehavior and then we'll publish v8.0.0 of 👍CommunityToolkit.Maui

@imsam67
Copy link

imsam67 commented Mar 18, 2024

Thanks for the update!

Just following up - since this has a breaking change to the API, and we're supper close to merging TouchBehavior, I'll be merging this PR after we merge TouchBehavior and then we'll publish v8.0.0 of CommunityToolkit.Maui 👍

brminnick
brminnick previously approved these changes Mar 25, 2024
Copy link
Collaborator

@brminnick brminnick left a comment

Choose a reason for hiding this comment

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

Thanks Kenji!!

@imsam67
Copy link

imsam67 commented Mar 25, 2024

Thanks Kenji for fixing the issue and thanks Brandon for getting this all wrapped up!

@brminnick brminnick enabled auto-merge (squash) March 25, 2024 23:23
@brminnick brminnick merged commit 460bf72 into CommunityToolkit:main Mar 25, 2024
7 checks passed
@imsam67
Copy link

imsam67 commented Mar 26, 2024

Just installed 99.0.0-preview1654 and I can confirm that the issue is now fixed. Thank you team!

@Kas-code
Copy link

@imsam67 @brminnick Please could you let me know how I can get hold of 99.0.0-preview1654 ?

@imsam67
Copy link

imsam67 commented Mar 28, 2024

@Kas-code The information is under Wiki > Usage section: https://github.com/CommunityToolkit/Maui/wiki/Preview-Packages

Just add the URL for the "Latest" feed to NuGet sources in Visual Studio > Tools > Options > NuGet Package Manager > Package Sources. BTW, they have a new version of preview now. HTH.

@Kas-code
Copy link

After updating to the latest preview, I can also confirm that the issue is fixed! Thanks @cat0363 , @brminnick , @bijington and everyone else who contributed!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
breaking change This label is used for PRs that include a breaking change
Projects
None yet
6 participants