Skip to content
Original file line number Diff line number Diff line change
Expand Up @@ -445,7 +445,7 @@ public void ShowPopupAsync_WithCustomOptions_AppliesOptions()
var popupPage = (PopupPage)navigation.ModalStack[0];
var popupPageContent = popupPage.Content;
var border = popupPageContent.PopupBorder;
var popup = border.Content;
var popup = (Popup)(border.Content ?? throw new InvalidOperationException("Content cannot be null"));

// Assert
Assert.NotNull(popup);
Expand Down Expand Up @@ -516,7 +516,7 @@ public void ShowPopupAsync_Shell_WithCustomOptions_AppliesOptions()
var popupPage = (PopupPage)shellNavigation.ModalStack[0];
var popupPageContent = popupPage.Content;
var border = popupPageContent.PopupBorder;
var popup = border.Content;
var popup = (ShortLivedSelfClosingPopup)(border.Content ?? throw new InvalidOperationException("Content cannot be null"));

// Assert
Assert.NotNull(popup);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -165,7 +165,7 @@ public void ShowPopupAsync_WithCustomOptions_AppliesOptions()
var popupPage = (PopupPage)navigation.ModalStack[0];
var popupPageLayout = popupPage.Content;
var border = popupPageLayout.PopupBorder;
var popup = border.Content;
var popup = (MockPopup)(border.Content ?? throw new InvalidOperationException("Content cannot be null"));

// Assert
Assert.NotNull(popup);
Expand Down Expand Up @@ -515,7 +515,6 @@ public async Task ClosePopupAsyncT_ShouldClosePopupUsingPageAndReturnResult()
}
}


class GarbageCollectionHeavySelfClosingPopup(MockPageViewModel viewModel, object? result = null) : MockSelfClosingPopup(viewModel, TimeSpan.FromMilliseconds(500), result)
{
protected override void HandlePopupOpened(object? sender, EventArgs e)
Expand Down
71 changes: 17 additions & 54 deletions src/CommunityToolkit.Maui/Views/Popup/Popup.shared.cs
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@

using CommunityToolkit.Maui.Extensions;

namespace CommunityToolkit.Maui.Views;
Expand All @@ -7,31 +8,6 @@ namespace CommunityToolkit.Maui.Views;
/// </summary>
public partial class Popup : ContentView
{
/// <summary>
/// Bindable property to set the margin between the <see cref="Popup"/> and the edge of the window
/// </summary>
public static new readonly BindableProperty MarginProperty = View.MarginProperty;

/// <summary>
/// Bindable property to set the padding between the <see cref="Popup"/> border and the <see cref="Popup"/> content
/// </summary>
public static new readonly BindableProperty PaddingProperty = ContentView.PaddingProperty;

/// <summary>
/// Bindable property to set the horizontal position of the <see cref="Popup"/> when displayed on screen
/// </summary>
public static new readonly BindableProperty HorizontalOptionsProperty = View.HorizontalOptionsProperty;

/// <summary>
/// Bindable property to set the vertical position of the <see cref="Popup"/> when displayed on screen
/// </summary>
public static new readonly BindableProperty VerticalOptionsProperty = View.VerticalOptionsProperty;

/// <summary>
/// Backing BindableProperty for the <see cref="CanBeDismissedByTappingOutsideOfPopup"/> property.
/// </summary>
public static readonly BindableProperty CanBeDismissedByTappingOutsideOfPopupProperty = BindableProperty.Create(nameof(CanBeDismissedByTappingOutsideOfPopup), typeof(bool), typeof(Popup), Options.DefaultPopupSettings.CanBeDismissedByTappingOutsideOfPopup);

/// <summary>
/// Initializes Popup
/// </summary>
Expand All @@ -55,51 +31,36 @@ public Popup()
public event EventHandler? Closed;

/// <summary>
/// Sets the margin between the <see cref="Popup"/> and the edge of the window
/// Gets or sets the margin between the <see cref="Popup"/> and the edge of the window.
/// </summary>
public new Thickness Margin
{
get => base.Margin;
set => base.Margin = value;
}
[BindableProperty]
public new partial Thickness Margin { get; set; } = Options.DefaultPopupSettings.Margin;

/// <summary>
/// Sets the padding between the <see cref="Popup"/> border and the <see cref="Popup"/> content
/// Gets or sets the padding between the <see cref="Popup"/> border and the <see cref="Popup"/> content.
/// </summary>
public new Thickness Padding
{
get => base.Padding;
set => base.Padding = value;
}
[BindableProperty]
public new partial Thickness Padding { get; set; } = Options.DefaultPopupSettings.Padding;

/// <summary>
/// Sets the horizontal position of the <see cref="Popup"/> when displayed on screen
/// Gets or sets the horizontal position of the <see cref="Popup"/> when displayed on screen.
/// </summary>
public new LayoutOptions HorizontalOptions
{
get => base.HorizontalOptions;
set => base.HorizontalOptions = value;
}
[BindableProperty]
public new partial LayoutOptions HorizontalOptions { get; set; } = Options.DefaultPopupSettings.HorizontalOptions;

/// <summary>
/// Sets the vertical position of the <see cref="Popup"/> when displayed on screen
/// Gets or sets the vertical position of the <see cref="Popup"/> when displayed on screen.
/// </summary>
public new LayoutOptions VerticalOptions
{
get => base.VerticalOptions;
set => base.VerticalOptions = value;
}
[BindableProperty]
public new partial LayoutOptions VerticalOptions { get; set; } = Options.DefaultPopupSettings.VerticalOptions;

/// <inheritdoc cref="IPopupOptions.CanBeDismissedByTappingOutsideOfPopup"/> />
/// <remarks>
/// When true and the user taps outside the popup, it will dismiss.
/// On Android - when false the hardware back button is disabled.
/// </remarks>
public bool CanBeDismissedByTappingOutsideOfPopup
{
get => (bool)GetValue(CanBeDismissedByTappingOutsideOfPopupProperty);
set => SetValue(CanBeDismissedByTappingOutsideOfPopupProperty, value);
}
[BindableProperty]
public partial bool CanBeDismissedByTappingOutsideOfPopup { get; set; } = Options.DefaultPopupSettings.CanBeDismissedByTappingOutsideOfPopup;

/// <summary>
/// Close the Popup.
Expand Down Expand Up @@ -148,5 +109,7 @@ public partial class Popup<T> : Popup
}

sealed class PopupNotFoundException() : InvalidPopupOperationException($"Unable to close popup: could not locate {nameof(PopupPage)}. {nameof(PopupExtensions.ShowPopup)} or {nameof(PopupExtensions.ShowPopupAsync)} must be called before {nameof(Popup.CloseAsync)}. If using a custom implementation of {nameof(Popup)}, override the {nameof(Popup.CloseAsync)} method");

sealed class PopupBlockedException(in Page currentVisibleModalPage) : InvalidPopupOperationException($"Unable to close Popup because it is blocked by the Modal Page {currentVisibleModalPage.GetType().FullName}. Please call `{nameof(Page.Navigation)}.{nameof(Page.Navigation.PopModalAsync)}()` to first remove {currentVisibleModalPage.GetType().FullName} from the {nameof(Page.Navigation.ModalStack)}");

class InvalidPopupOperationException(in string message) : InvalidOperationException(message);
62 changes: 11 additions & 51 deletions src/CommunityToolkit.Maui/Views/Popup/PopupOptions.shared.cs
Original file line number Diff line number Diff line change
Expand Up @@ -7,31 +7,6 @@ namespace CommunityToolkit.Maui;
/// </summary>
public partial class PopupOptions : BindableObject, IPopupOptions
{
/// <summary>
/// Backing BindableProperty for the <see cref="CanBeDismissedByTappingOutsideOfPopup"/> property.
/// </summary>
public static readonly BindableProperty CanBeDismissedByTappingOutsideOfPopupProperty = BindableProperty.Create(nameof(CanBeDismissedByTappingOutsideOfPopup), typeof(bool), typeof(PopupOptions), Options.DefaultPopupOptionsSettings.CanBeDismissedByTappingOutsideOfPopup);

/// <summary>
/// Backing BindableProperty for the <see cref="OnTappingOutsideOfPopup"/> property.
/// </summary>
public static readonly BindableProperty OnTappingOutsideOfPopupProperty = BindableProperty.Create(nameof(OnTappingOutsideOfPopup), typeof(Action), typeof(PopupOptions), Options.DefaultPopupOptionsSettings.OnTappingOutsideOfPopup);

/// <summary>
/// Backing BindableProperty for the <see cref="PageOverlayColor"/> property.
/// </summary>
public static readonly BindableProperty PageOverlayColorProperty = BindableProperty.Create(nameof(PageOverlayColor), typeof(Color), typeof(PopupOptions), Options.DefaultPopupOptionsSettings.PageOverlayColor);

/// <summary>
/// Backing BindableProperty for the <see cref="Shape"/> property.
/// </summary>
public static readonly BindableProperty ShapeProperty = BindableProperty.Create(nameof(Shape), typeof(Shape), typeof(PopupOptions), Options.DefaultPopupOptionsSettings.Shape);

/// <summary>
/// Backing BindableProperty for the <see cref="Shadow"/> property.
/// </summary>
public static readonly BindableProperty ShadowProperty = BindableProperty.Create(nameof(Shadow), typeof(Shadow), typeof(PopupOptions), Options.DefaultPopupOptionsSettings.Shadow);

/// <summary>
/// An empty instance of <see cref="IPopupOptions"/> containing default values.
/// </summary>
Expand All @@ -42,37 +17,22 @@ public partial class PopupOptions : BindableObject, IPopupOptions
/// When true and the user taps outside the popup, it will dismiss.
/// On Android - when false the hardware back button is disabled.
/// </remarks>
public bool CanBeDismissedByTappingOutsideOfPopup
{
get => (bool)GetValue(CanBeDismissedByTappingOutsideOfPopupProperty);
set => SetValue(CanBeDismissedByTappingOutsideOfPopupProperty, value);
}
[BindableProperty]
public partial bool CanBeDismissedByTappingOutsideOfPopup { get; set; } = Options.DefaultPopupOptionsSettings.CanBeDismissedByTappingOutsideOfPopup;

/// <inheritdoc/>
public Color PageOverlayColor
{
get => (Color)GetValue(PageOverlayColorProperty);
set => SetValue(PageOverlayColorProperty, value);
}
[BindableProperty]
public partial Action? OnTappingOutsideOfPopup { get; set; } = Options.DefaultPopupOptionsSettings.OnTappingOutsideOfPopup;

/// <inheritdoc/>
public Action? OnTappingOutsideOfPopup
{
get => (Action?)GetValue(OnTappingOutsideOfPopupProperty);
set => SetValue(OnTappingOutsideOfPopupProperty, value);
}
[BindableProperty]
public partial Color PageOverlayColor { get; set; } = Options.DefaultPopupOptionsSettings.PageOverlayColor;

/// <inheritdoc/>
public Shape? Shape
{
get => (Shape?)GetValue(ShapeProperty);
set => SetValue(ShapeProperty, value);
}
[BindableProperty]
public partial Shape? Shape { get; set; } = Options.DefaultPopupOptionsSettings.Shape;

/// <inheritdoc/>
public Shadow? Shadow
{
get => (Shadow?)GetValue(ShadowProperty);
set => SetValue(ShadowProperty, value);
}
/// <inheritdoc cref="IPopupOptions.Shadow"/>
[BindableProperty]
public partial Shadow? Shadow { get; set; } = Options.DefaultPopupOptionsSettings.Shadow;
}
Loading