diff --git a/samples/CommunityToolkit.Maui.Sample/MauiProgram.cs b/samples/CommunityToolkit.Maui.Sample/MauiProgram.cs index b4f7893a6a..e5fed83b75 100644 --- a/samples/CommunityToolkit.Maui.Sample/MauiProgram.cs +++ b/samples/CommunityToolkit.Maui.Sample/MauiProgram.cs @@ -55,6 +55,8 @@ public static MauiApp CreateMauiApp() .UseMauiCommunityToolkit(static options => { options.SetShouldEnableSnackbarOnWindows(true); + options.SetPopupDefaults(new DefaultPopupSettings()); + options.SetPopupOptionsDefaults(new DefaultPopupOptionsSettings()); }) #else .UseMauiCommunityToolkit(static options => diff --git a/src/CommunityToolkit.Maui.UnitTests/BaseTest.cs b/src/CommunityToolkit.Maui.UnitTests/BaseTest.cs index 5c0489fdc9..7ee48febe3 100644 --- a/src/CommunityToolkit.Maui.UnitTests/BaseTest.cs +++ b/src/CommunityToolkit.Maui.UnitTests/BaseTest.cs @@ -84,6 +84,8 @@ protected virtual void Dispose(bool isDisposing) options.SetShouldSuppressExceptionsInAnimations(false); options.SetShouldSuppressExceptionsInBehaviors(false); options.SetShouldSuppressExceptionsInConverters(false); + options.SetPopupDefaults(new DefaultPopupSettings()); + options.SetPopupOptionsDefaults(new DefaultPopupOptionsSettings()); // Restore default MediaElementOptions var mediaElementOptions = new MediaElementOptions(); diff --git a/src/CommunityToolkit.Maui.UnitTests/BaseViewTest.cs b/src/CommunityToolkit.Maui.UnitTests/BaseViewTest.cs index f9e6bde151..341eac27a1 100644 --- a/src/CommunityToolkit.Maui.UnitTests/BaseViewTest.cs +++ b/src/CommunityToolkit.Maui.UnitTests/BaseViewTest.cs @@ -74,7 +74,6 @@ static void InitializeServicesAndSetMockApplication(out IServiceProvider service #endregion #region Register Services for PopupServiceTests - appBuilder.Services.AddTransientPopup(); appBuilder.Services.AddTransientPopup(); appBuilder.Services.AddTransientPopup(); diff --git a/src/CommunityToolkit.Maui.UnitTests/Extensions/AppBuilderExtensionsTests.cs b/src/CommunityToolkit.Maui.UnitTests/Extensions/AppBuilderExtensionsTests.cs index 5f2f6a752c..c3e9f61aea 100644 --- a/src/CommunityToolkit.Maui.UnitTests/Extensions/AppBuilderExtensionsTests.cs +++ b/src/CommunityToolkit.Maui.UnitTests/Extensions/AppBuilderExtensionsTests.cs @@ -21,6 +21,8 @@ public void ConfirmOptionsDefaultValue() Assert.False(Options.ShouldSuppressExceptionsInBehaviors); Assert.False(Options.ShouldSuppressExceptionsInConverters); Assert.False(isAndroidDialogFragmentServiceInitialized); + Assert.Equal(Options.DefaultPopupSettings, new DefaultPopupSettings()); + Assert.Equal(Options.DefaultPopupOptionsSettings, new DefaultPopupOptionsSettings()); Core.AppBuilderExtensions.ShouldUseStatusBarBehaviorOnAndroidModalPageOptionCompleted -= HandleShouldUseStatusBarBehaviorOnAndroidModalPageOptionCompleted; @@ -50,6 +52,8 @@ public void ConfirmDefaultValueRemainWhenOptionsNull() Assert.False(Options.ShouldSuppressExceptionsInBehaviors); Assert.False(Options.ShouldSuppressExceptionsInConverters); Assert.True(isAndroidDialogFragmentServiceInitialized); + Assert.Equal(Options.DefaultPopupSettings, new DefaultPopupSettings()); + Assert.Equal(Options.DefaultPopupOptionsSettings, new DefaultPopupOptionsSettings()); void HandleShouldUseStatusBarBehaviorOnAndroidModalPageOptionCompleted(object? sender, EventArgs e) { @@ -88,6 +92,25 @@ public void UseMauiCommunityToolkit_ShouldAssignValues() // Arrange var builder = MauiApp.CreateBuilder(); bool isAndroidDialogFragmentServiceInitialized = false; + var defaultPopupSettings = new DefaultPopupSettings + { + CanBeDismissedByTappingOutsideOfPopup = true, + BackgroundColor = Colors.Orange, + HorizontalOptions = LayoutOptions.End, + VerticalOptions = LayoutOptions.Start, + Margin = 72, + Padding = 4 + }; + + var defaultPopupOptionsSettings = new DefaultPopupOptionsSettings + { + CanBeDismissedByTappingOutsideOfPopup = true, + OnTappingOutsideOfPopup = () => Console.WriteLine("Hello World"), + PageOverlayColor = Colors.Green, + Shadow = null, + Shape = null + }; + Core.AppBuilderExtensions.ShouldUseStatusBarBehaviorOnAndroidModalPageOptionCompleted += HandleShouldUseStatusBarBehaviorOnAndroidModalPageOptionCompleted; // Act @@ -98,6 +121,8 @@ public void UseMauiCommunityToolkit_ShouldAssignValues() options.SetShouldSuppressExceptionsInBehaviors(!Options.ShouldSuppressExceptionsInBehaviors); options.SetShouldSuppressExceptionsInConverters(!Options.ShouldSuppressExceptionsInConverters); options.SetShouldUseStatusBarBehaviorOnAndroidModalPage(!Core.Options.ShouldUseStatusBarBehaviorOnAndroidModalPage); + options.SetPopupDefaults(defaultPopupSettings); + options.SetPopupOptionsDefaults(defaultPopupOptionsSettings); }); // Assert @@ -107,6 +132,8 @@ public void UseMauiCommunityToolkit_ShouldAssignValues() Assert.True(Options.ShouldSuppressExceptionsInBehaviors); Assert.True(Options.ShouldSuppressExceptionsInConverters); Assert.False(isAndroidDialogFragmentServiceInitialized); + Assert.Equal(defaultPopupSettings, Options.DefaultPopupSettings); + Assert.Equal(defaultPopupOptionsSettings, Options.DefaultPopupOptionsSettings); Core.AppBuilderExtensions.ShouldUseStatusBarBehaviorOnAndroidModalPageOptionCompleted -= HandleShouldUseStatusBarBehaviorOnAndroidModalPageOptionCompleted; diff --git a/src/CommunityToolkit.Maui.UnitTests/Extensions/PopupExtensionsTests.cs b/src/CommunityToolkit.Maui.UnitTests/Extensions/PopupExtensionsTests.cs index 3ae1b7e10a..31c419942d 100644 --- a/src/CommunityToolkit.Maui.UnitTests/Extensions/PopupExtensionsTests.cs +++ b/src/CommunityToolkit.Maui.UnitTests/Extensions/PopupExtensionsTests.cs @@ -184,6 +184,7 @@ public void ShowPopupAsync_WithViewType_ShowsPopup() public void ShowPopupAsync_WithViewType_SetsCorrectDefaults() { // Arrange + Border popupBorder; PopupPage popupPage; Popup autogeneratedPopup; var label = new Label(); @@ -192,13 +193,16 @@ public void ShowPopupAsync_WithViewType_SetsCorrectDefaults() navigation.ShowPopup(label); popupPage = (PopupPage)navigation.ModalStack[0]; + popupBorder = popupPage.Content.PopupBorder; autogeneratedPopup = (Popup)(popupPage.Content.PopupBorder.Content ?? throw new InvalidOperationException("Border Content cannot be null")); // Assert - Assert.Equal(PopupDefaults.BackgroundColor, autogeneratedPopup.BackgroundColor); - Assert.Equal(PopupDefaults.HorizontalOptions, autogeneratedPopup.HorizontalOptions); - Assert.Equal(PopupDefaults.VerticalOptions, autogeneratedPopup.VerticalOptions); - Assert.Equal(PopupDefaults.Padding, autogeneratedPopup.Padding); + Assert.Equal(new Thickness(30), popupBorder.Margin); + Assert.Equal(LayoutOptions.Center, popupBorder.VerticalOptions); + Assert.Equal(LayoutOptions.Center, popupBorder.HorizontalOptions); + Assert.Equal(new Thickness(15), autogeneratedPopup.Padding); + Assert.Equal(Colors.White, autogeneratedPopup.BackgroundColor); + Assert.True(autogeneratedPopup.CanBeDismissedByTappingOutsideOfPopup); } [Fact] @@ -465,7 +469,6 @@ public void ShowPopupAsync_WithCustomOptions_AppliesOptions() // Verify Border Bindings to Border Assert.Equal(popup.BindingContext, border.BindingContext); Assert.Equal(popup.Margin, border.Margin); - Assert.Equal(((IPaddingElement)popup).Padding, border.Padding); Assert.Equal(popup.Background, border.Background); Assert.Equal(popup.BackgroundColor, border.BackgroundColor); Assert.Equal(popup.HorizontalOptions, border.HorizontalOptions); @@ -537,7 +540,6 @@ public void ShowPopupAsync_Shell_WithCustomOptions_AppliesOptions() // Verify Border Bindings to Border Assert.Equal(popup.BindingContext, border.BindingContext); Assert.Equal(popup.Margin, border.Margin); - Assert.Equal(((IPaddingElement)popup).Padding, border.Padding); Assert.Equal(popup.Background, border.Background); Assert.Equal(popup.BackgroundColor, border.BackgroundColor); Assert.Equal(popup.HorizontalOptions, border.HorizontalOptions); @@ -563,6 +565,8 @@ public void ShowPopupAsyncWithView_WithCustomOptions_AppliesOptions() var view = new Grid { + Margin = 20, + BackgroundColor = Colors.Orange, HorizontalOptions = LayoutOptions.End, VerticalOptions = LayoutOptions.Start, }; @@ -606,10 +610,9 @@ public void ShowPopupAsyncWithView_WithCustomOptions_AppliesOptions() Assert.Equal(view.VerticalOptions, border.VerticalOptions); Assert.Equal(view.HorizontalOptions, border.HorizontalOptions); - // Verify Border Bindings to Border + // Verify Popup Bindings to Border Assert.Equal(popup.BindingContext, border.BindingContext); Assert.Equal(popup.Margin, border.Margin); - Assert.Equal(((IPaddingElement)popup).Padding, border.Padding); Assert.Equal(popup.Background, border.Background); Assert.Equal(popup.BackgroundColor, border.BackgroundColor); Assert.Equal(popup.HorizontalOptions, border.HorizontalOptions); @@ -676,22 +679,17 @@ public void ShowPopupAsyncWithView_Shell_WithCustomOptions_AppliesOptions() Assert.Equal(viewWithQueryable.Background, popup.Background); Assert.Equal(viewWithQueryable.BackgroundColor, popup.BackgroundColor); Assert.Equal(viewWithQueryable.Margin, popup.Margin); - Assert.Equal(viewWithQueryable.VerticalOptions, popup.VerticalOptions); - Assert.Equal(viewWithQueryable.HorizontalOptions, popup.HorizontalOptions); // Verify View options Binding to Border Assert.Equal(viewWithQueryable.BindingContext, border.BindingContext); Assert.Equal(viewWithQueryable.Background, border.Background); Assert.Equal(viewWithQueryable.BackgroundColor, border.BackgroundColor); - Assert.Equal(viewWithQueryable.Padding, border.Padding); - Assert.Equal(viewWithQueryable.Margin, border.Margin); Assert.Equal(viewWithQueryable.VerticalOptions, border.VerticalOptions); Assert.Equal(viewWithQueryable.HorizontalOptions, border.HorizontalOptions); - // Verify Border Bindings to Border + // Verify Border Bindings to Popup Assert.Equal(popup.BindingContext, border.BindingContext); Assert.Equal(popup.Margin, border.Margin); - Assert.Equal(((IPaddingElement)popup).Padding, border.Padding); Assert.Equal(popup.Background, border.Background); Assert.Equal(popup.BackgroundColor, border.BackgroundColor); Assert.Equal(popup.HorizontalOptions, border.HorizontalOptions); diff --git a/src/CommunityToolkit.Maui.UnitTests/Views/Popup/DefaultPopupOptionsSettingsTests.cs b/src/CommunityToolkit.Maui.UnitTests/Views/Popup/DefaultPopupOptionsSettingsTests.cs new file mode 100644 index 0000000000..4d8b8f24e2 --- /dev/null +++ b/src/CommunityToolkit.Maui.UnitTests/Views/Popup/DefaultPopupOptionsSettingsTests.cs @@ -0,0 +1,287 @@ +using CommunityToolkit.Maui.Views; +using Microsoft.Maui.Controls.Shapes; +using Nito.AsyncEx; +using Xunit; + +namespace CommunityToolkit.Maui.UnitTests.Views; + +#pragma warning disable CA1416 +public class DefaultPopupOptionsSettingsTests : BaseViewTest +{ + readonly INavigation navigation; + + public DefaultPopupOptionsSettingsTests() + { + if (Application.Current?.Windows[0].Page is not Page page) + { + throw new InvalidOperationException("Page cannot be null"); + } + + navigation = page.Navigation; + } + + [Fact] + public void Popup_SetPopupOptionsDefaultsNotCalled_UsesPopupOptionsDefaults() + { + // Arrange + var popupPage = new PopupPage(new Popup(), null); + var popupBorder = popupPage.Content.PopupBorder; + + var tapGestureRecognizer = GetTapOutsideGestureRecognizer(popupPage); + + // Assert + Assert.True(tapGestureRecognizer.Command?.CanExecute(null)); + Assert.Equal(2, popupBorder.StrokeThickness); + Assert.Equal(Colors.LightGray, popupBorder.Stroke); + Assert.Equal(Colors.Black.WithAlpha(0.3f), popupPage.BackgroundColor); + + Assert.Equal(Colors.Black, popupBorder.Shadow.Brush); + Assert.Equal(new(20, 20), popupBorder.Shadow.Offset); + Assert.Equal(40, popupBorder.Shadow.Radius); + Assert.Equal(0.8f, popupBorder.Shadow.Opacity); + + Assert.Equal(new CornerRadius(20, 20, 20, 20), ((RoundRectangle?)popupBorder.StrokeShape)?.CornerRadius); + Assert.Equal(2, ((RoundRectangle?)popupBorder.StrokeShape)?.StrokeThickness); + Assert.Equal(Colors.LightGray, ((RoundRectangle?)popupBorder.StrokeShape)?.Stroke); + } + + [Fact] + public void Popup_SetPopupOptionsNotCalled_PopupOptionsEmptyUsed_UsesPopupOptionsDefaults() + { + // Arrange + var popupPage = new PopupPage(new Popup(), PopupOptions.Empty); + var popupBorder = popupPage.Content.PopupBorder; + + var tapGestureRecognizer = GetTapOutsideGestureRecognizer(popupPage); + + // Assert + Assert.True(tapGestureRecognizer.Command?.CanExecute(null)); + Assert.Equal(2, popupBorder.StrokeThickness); + Assert.Equal(Colors.LightGray, popupBorder.Stroke); + Assert.Equal(Colors.Black.WithAlpha(0.3f), popupPage.BackgroundColor); + + Assert.Equal(Colors.Black, popupBorder.Shadow.Brush); + Assert.Equal(new(20, 20), popupBorder.Shadow.Offset); + Assert.Equal(40, popupBorder.Shadow.Radius); + Assert.Equal(0.8f, popupBorder.Shadow.Opacity); + + Assert.Equal(new CornerRadius(20, 20, 20, 20), ((RoundRectangle?)popupBorder.StrokeShape)?.CornerRadius); + Assert.Equal(2, ((RoundRectangle?)popupBorder.StrokeShape)?.StrokeThickness); + Assert.Equal(Colors.LightGray, ((RoundRectangle?)popupBorder.StrokeShape)?.Stroke); + } + + [Fact] + public void Popup_SetPopupDefaultsCalled_UsesDefaultPopupOptionsSettings() + { + // Arrange + var hasOnTappingOutsideOfPopupExecuted = false; + + var defaultPopupSettings = new DefaultPopupOptionsSettings + { + CanBeDismissedByTappingOutsideOfPopup = true, + OnTappingOutsideOfPopup = () => hasOnTappingOutsideOfPopupExecuted = true, + PageOverlayColor = Colors.Orange, + Shadow = null, + Shape = null + }; + + var builder = MauiApp.CreateBuilder(); + builder.UseMauiCommunityToolkit(options => { options.SetPopupOptionsDefaults(defaultPopupSettings); }); + + var popupPage = new PopupPage(new Popup(), null); + var popupBorder = popupPage.Content.PopupBorder; + var tapGestureRecognizer = GetTapOutsideGestureRecognizer(popupPage); + + // Act + try + { + // Run using AsyncContext to catch Exception thrown by fire-and-forget ICommand.Execute + AsyncContext.Run(() => { tapGestureRecognizer.Command?.Execute(null); }); + } + catch (PopupNotFoundException) // PopupNotFoundException is expected here because `ShowPopup` was never called + { + } + + // // Assert + Assert.True(tapGestureRecognizer.Command?.CanExecute(null)); + Assert.True(hasOnTappingOutsideOfPopupExecuted); + Assert.Equal(defaultPopupSettings.PageOverlayColor, popupPage.BackgroundColor); + Assert.Equal(defaultPopupSettings.Shadow, popupBorder.Shadow); + Assert.Equal(defaultPopupSettings.Shape, popupBorder.StrokeShape); + } + + [Fact] + public void Popup_SetPopupDefaultsCalled_PopupOptionsOverridden_UsesProvidedPopupOptionsSettings() + { + // Arrange + var hasOnTappingOutsideOfPopupExecuted = false; + + var defaultPopupSettings = new DefaultPopupOptionsSettings + { + CanBeDismissedByTappingOutsideOfPopup = true, + OnTappingOutsideOfPopup = () => hasOnTappingOutsideOfPopupExecuted = true, + PageOverlayColor = Colors.Orange, + Shadow = null, + Shape = null + }; + + var builder = MauiApp.CreateBuilder(); + builder.UseMauiCommunityToolkit(options => { options.SetPopupOptionsDefaults(new DefaultPopupOptionsSettings()); }); + + var popupPage = new PopupPage(new Popup(), defaultPopupSettings); + var popupBorder = popupPage.Content.PopupBorder; + var tapGestureRecognizer = GetTapOutsideGestureRecognizer(popupPage); + + // Act + try + { + // Run using AsyncContext to catch Exception thrown by fire-and-forget ICommand.Execute + AsyncContext.Run(() => { tapGestureRecognizer.Command?.Execute(null); }); + } + catch (PopupNotFoundException) // PopupNotFoundException is expected here because `ShowPopup` was never called + { + } + + // // Assert + Assert.True(tapGestureRecognizer.Command?.CanExecute(null)); + Assert.True(hasOnTappingOutsideOfPopupExecuted); + Assert.Equal(defaultPopupSettings.PageOverlayColor, popupPage.BackgroundColor); + Assert.Equal(defaultPopupSettings.Shadow, popupBorder.Shadow); + Assert.Equal(defaultPopupSettings.Shape, popupBorder.StrokeShape); + } + + [Fact] + public void View_SetPopupOptionsDefaultsNotCalled_UsesPopupOptionsDefaults() + { + // Arrange + var popupPage = new PopupPage(new View(), null); + var popupBorder = popupPage.Content.PopupBorder; + + var tapGestureRecognizer = GetTapOutsideGestureRecognizer(popupPage); + + // Assert + Assert.True(tapGestureRecognizer.Command?.CanExecute(null)); + Assert.Equal(2, popupBorder.StrokeThickness); + Assert.Equal(Colors.LightGray, popupBorder.Stroke); + Assert.Equal(Colors.Black.WithAlpha(0.3f), popupPage.BackgroundColor); + + Assert.Equal(Colors.Black, popupBorder.Shadow.Brush); + Assert.Equal(new(20, 20), popupBorder.Shadow.Offset); + Assert.Equal(40, popupBorder.Shadow.Radius); + Assert.Equal(0.8f, popupBorder.Shadow.Opacity); + + Assert.Equal(new CornerRadius(20, 20, 20, 20), ((RoundRectangle?)popupBorder.StrokeShape)?.CornerRadius); + Assert.Equal(2, ((RoundRectangle?)popupBorder.StrokeShape)?.StrokeThickness); + Assert.Equal(Colors.LightGray, ((RoundRectangle?)popupBorder.StrokeShape)?.Stroke); + } + + [Fact] + public void View_SetPopupOptionsNotCalled_PopupOptionsEmptyUsed_UsesPopupOptionsDefaults() + { + // Arrange + var popupPage = new PopupPage(new View(), PopupOptions.Empty); + var popupBorder = popupPage.Content.PopupBorder; + + var tapGestureRecognizer = GetTapOutsideGestureRecognizer(popupPage); + +// Assert + Assert.True(tapGestureRecognizer.Command?.CanExecute(null)); + Assert.Equal(2, popupBorder.StrokeThickness); + Assert.Equal(Colors.LightGray, popupBorder.Stroke); + Assert.Equal(Colors.Black.WithAlpha(0.3f), popupPage.BackgroundColor); + + Assert.Equal(Colors.Black, popupBorder.Shadow.Brush); + Assert.Equal(new(20, 20), popupBorder.Shadow.Offset); + Assert.Equal(40, popupBorder.Shadow.Radius); + Assert.Equal(0.8f, popupBorder.Shadow.Opacity); + + Assert.Equal(new CornerRadius(20, 20, 20, 20), ((RoundRectangle?)popupBorder.StrokeShape)?.CornerRadius); + Assert.Equal(2, ((RoundRectangle?)popupBorder.StrokeShape)?.StrokeThickness); + Assert.Equal(Colors.LightGray, ((RoundRectangle?)popupBorder.StrokeShape)?.Stroke); + } + + [Fact] + public void View_SetPopupDefaultsCalled_UsesDefaultPopupOptionsSettings() + { + // Arrange + var hasOnTappingOutsideOfPopupExecuted = false; + + var defaultPopupSettings = new DefaultPopupOptionsSettings + { + CanBeDismissedByTappingOutsideOfPopup = true, + OnTappingOutsideOfPopup = () => hasOnTappingOutsideOfPopupExecuted = true, + PageOverlayColor = Colors.Orange, + Shadow = null, + Shape = null + }; + + var builder = MauiApp.CreateBuilder(); + builder.UseMauiCommunityToolkit(options => { options.SetPopupOptionsDefaults(defaultPopupSettings); }); + + var popupPage = new PopupPage(new View(), null); + var popupBorder = popupPage.Content.PopupBorder; + var tapGestureRecognizer = GetTapOutsideGestureRecognizer(popupPage); + + // Act + try + { + // Run using AsyncContext to catch Exception thrown by fire-and-forget ICommand.Execute + AsyncContext.Run(() => { tapGestureRecognizer.Command?.Execute(null); }); + } + catch (PopupNotFoundException) // PopupNotFoundException is expected here because `ShowPopup` was never called + { + } + + // // Assert + Assert.True(tapGestureRecognizer.Command?.CanExecute(null)); + Assert.True(hasOnTappingOutsideOfPopupExecuted); + Assert.Equal(defaultPopupSettings.PageOverlayColor, popupPage.BackgroundColor); + Assert.Equal(defaultPopupSettings.Shadow, popupBorder.Shadow); + Assert.Equal(defaultPopupSettings.Shape, popupBorder.StrokeShape); + } + + [Fact] + public void View_SetPopupDefaultsCalled_PopupOptionsOverridden_UsesProvidedPopupOptionsSettings() + { + // Arrange + var hasOnTappingOutsideOfPopupExecuted = false; + + var defaultPopupSettings = new DefaultPopupOptionsSettings + { + CanBeDismissedByTappingOutsideOfPopup = true, + OnTappingOutsideOfPopup = () => hasOnTappingOutsideOfPopupExecuted = true, + PageOverlayColor = Colors.Orange, + Shadow = null, + Shape = null + }; + + var builder = MauiApp.CreateBuilder(); + builder.UseMauiCommunityToolkit(options => { options.SetPopupOptionsDefaults(new DefaultPopupOptionsSettings()); }); + + var popupPage = new PopupPage(new View(), defaultPopupSettings); + var popupBorder = popupPage.Content.PopupBorder; + var tapGestureRecognizer = GetTapOutsideGestureRecognizer(popupPage); + + // Act + try + { + // Run using AsyncContext to catch Exception thrown by fire-and-forget ICommand.Execute + AsyncContext.Run(() => { tapGestureRecognizer.Command?.Execute(null); }); + } + catch (PopupNotFoundException) // PopupNotFoundException is expected here because `ShowPopup` was never called + { + } + + // // Assert + Assert.True(tapGestureRecognizer.Command?.CanExecute(null)); + Assert.True(hasOnTappingOutsideOfPopupExecuted); + Assert.Equal(defaultPopupSettings.PageOverlayColor, popupPage.BackgroundColor); + Assert.Equal(defaultPopupSettings.Shadow, popupBorder.Shadow); + Assert.Equal(defaultPopupSettings.Shape, popupBorder.StrokeShape); + } + + + static TapGestureRecognizer GetTapOutsideGestureRecognizer(PopupPage popupPage) => + (TapGestureRecognizer)popupPage.Content.Children.OfType().Single().GestureRecognizers[0]; +} +#pragma warning restore CA1416 \ No newline at end of file diff --git a/src/CommunityToolkit.Maui.UnitTests/Views/Popup/DefaultPopupSettingsTests.cs b/src/CommunityToolkit.Maui.UnitTests/Views/Popup/DefaultPopupSettingsTests.cs new file mode 100644 index 0000000000..ff0e8d39d4 --- /dev/null +++ b/src/CommunityToolkit.Maui.UnitTests/Views/Popup/DefaultPopupSettingsTests.cs @@ -0,0 +1,101 @@ +using CommunityToolkit.Maui.Views; +using Xunit; + +namespace CommunityToolkit.Maui.UnitTests.Views; + +#pragma warning disable CA1416 +public class DefaultPopupSettingsTests : BaseTest +{ + [Fact] + public void Popup_SetPopupDefaultsNotCalled_UsesPopupDefaults() + { + // Arrange + var popupPage = new PopupPage(new Popup(), PopupOptions.Empty); + var popup = (Popup)(popupPage.Content.PopupBorder.Content ?? throw new InvalidOperationException("Popup cannot be null")); + + // Assert + Assert.True(popup.CanBeDismissedByTappingOutsideOfPopup); + Assert.Equal(new Thickness(30), popup.Margin); + Assert.Equal(new Thickness(15), popup.Padding); + Assert.Equal(LayoutOptions.Center, popup.HorizontalOptions); + Assert.Equal(LayoutOptions.Center, popup.VerticalOptions); + Assert.Equal(Colors.White, popup.BackgroundColor); + } + + [Fact] + public void Popup_SetPopupDefaultsCalled_UsesDefaultPopupSettings() + { + // Arrange + var defaultPopupSettings = new DefaultPopupSettings + { + CanBeDismissedByTappingOutsideOfPopup = true, + BackgroundColor = Colors.Orange, + HorizontalOptions = LayoutOptions.End, + VerticalOptions = LayoutOptions.Start, + Margin = 72, + Padding = 4 + }; + + var builder = MauiApp.CreateBuilder(); + builder.UseMauiCommunityToolkit(options => { options.SetPopupDefaults(defaultPopupSettings); }); + + var popupPage = new PopupPage(new Popup(), PopupOptions.Empty); + var popup = (Popup)(popupPage.Content.PopupBorder.Content ?? throw new InvalidOperationException("Popup cannot be null")); + + // Act // Assert + Assert.Equal(defaultPopupSettings.CanBeDismissedByTappingOutsideOfPopup, popup.CanBeDismissedByTappingOutsideOfPopup); + Assert.Equal(defaultPopupSettings.Margin, popup.Margin); + Assert.Equal(defaultPopupSettings.Padding, popup.Padding); + Assert.Equal(defaultPopupSettings.HorizontalOptions, popup.HorizontalOptions); + Assert.Equal(defaultPopupSettings.VerticalOptions, popup.VerticalOptions); + Assert.Equal(defaultPopupSettings.BackgroundColor, popup.BackgroundColor); + } + + [Fact] + public void View_SetPopupDefaultsNotCalled_UsesPopupDefaults() + { + // Arrange + var popupPage = new PopupPage(new View(), PopupOptions.Empty); + var popupBorder = popupPage.Content.PopupBorder; + var popup = (Popup)(popupBorder.Content ?? throw new InvalidOperationException("Popup cannot be null")); + + // Assert + Assert.True(popup.CanBeDismissedByTappingOutsideOfPopup); + Assert.Equal(new Thickness(30), popup.Margin); + Assert.Equal(new Thickness(15), popup.Padding); + Assert.Equal(LayoutOptions.Center, popupBorder.HorizontalOptions); + Assert.Equal(LayoutOptions.Center, popupBorder.VerticalOptions); + Assert.Equal(Colors.White, popup.BackgroundColor); + } + + [Fact] + public void View_SetPopupDefaultsCalled_UsesDefaultPopupSettings() + { + // Arrange + var defaultPopupSettings = new DefaultPopupSettings + { + CanBeDismissedByTappingOutsideOfPopup = true, + BackgroundColor = Colors.Orange, + HorizontalOptions = LayoutOptions.End, + VerticalOptions = LayoutOptions.Start, + Margin = 72, + Padding = 4 + }; + + var builder = MauiApp.CreateBuilder(); + builder.UseMauiCommunityToolkit(options => { options.SetPopupDefaults(defaultPopupSettings); }); + + var popupPage = new PopupPage(new View(), PopupOptions.Empty); + var popupBorder = popupPage.Content.PopupBorder; + var popup = (Popup)(popupBorder.Content ?? throw new InvalidOperationException("Popup cannot be null")); + + // Act // Assert + Assert.Equal(defaultPopupSettings.Padding, popup.Padding); + Assert.Equal(defaultPopupSettings.BackgroundColor, popup.BackgroundColor); + Assert.Equal(defaultPopupSettings.CanBeDismissedByTappingOutsideOfPopup, popup.CanBeDismissedByTappingOutsideOfPopup); + Assert.Equal(defaultPopupSettings.Margin, popupBorder.Margin); + Assert.Equal(defaultPopupSettings.VerticalOptions, popupBorder.VerticalOptions); + Assert.Equal(defaultPopupSettings.HorizontalOptions, popupBorder.HorizontalOptions); + } +} +#pragma warning restore CA1416 \ No newline at end of file diff --git a/src/CommunityToolkit.Maui.UnitTests/Views/Popup/PopupOptionsTests.cs b/src/CommunityToolkit.Maui.UnitTests/Views/Popup/PopupOptionsTests.cs index 20c7b39119..51f61371bd 100644 --- a/src/CommunityToolkit.Maui.UnitTests/Views/Popup/PopupOptionsTests.cs +++ b/src/CommunityToolkit.Maui.UnitTests/Views/Popup/PopupOptionsTests.cs @@ -9,7 +9,7 @@ public class PopupOptionsTests : BaseTest public void CanBeDismissedByTappingOutsideOfPopup_DefaultValue_ShouldBeTrue() { var popupOptions = new PopupOptions(); - Assert.Equal(PopupOptionsDefaults.CanBeDismissedByTappingOutsideOfPopup, popupOptions.CanBeDismissedByTappingOutsideOfPopup); + Assert.True(popupOptions.CanBeDismissedByTappingOutsideOfPopup); } [Fact] @@ -24,7 +24,11 @@ public void CanBeDismissedByTappingOutsideOfPopup_SetValue_ShouldBeUpdated() public void Shadow_DefaultValue_ShouldBeTrue() { var popupOptions = new PopupOptions(); - Assert.Equal(PopupOptionsDefaults.Shadow, popupOptions.Shadow); + + Assert.Equal(Colors.Black, popupOptions.Shadow?.Brush); + Assert.Equal(new(20, 20), popupOptions.Shadow?.Offset); + Assert.Equal(40, popupOptions.Shadow?.Radius); + Assert.Equal(0.8f, popupOptions.Shadow?.Opacity); } [Fact] @@ -39,7 +43,7 @@ public void Shadow_SetValue_ShouldBeUpdated() public void PageOverlayColor_DefaultValue_ShouldBeDefaultColor() { var popupOptions = new PopupOptions(); - Assert.Equal(PopupOptionsDefaults.PageOverlayColor, popupOptions.PageOverlayColor); + Assert.Equal(Colors.Black.WithAlpha(0.3f), popupOptions.PageOverlayColor); } [Fact] @@ -55,14 +59,14 @@ public void PageOverlayColor_SetValue_ShouldBeUpdated() public void BorderStroke_DefaultValue_ShouldBeDefaultStroke() { var popupOptions = new PopupOptions(); - Assert.Equal(PopupOptionsDefaults.BorderStroke, popupOptions.Shape?.Stroke); + Assert.Equal(Colors.LightGray, popupOptions.Shape?.Stroke); } [Fact] public void OnTappingOutsideOfPopup_DefaultValue_ShouldBeNull() { var popupOptions = new PopupOptions(); - Assert.Equal(PopupOptionsDefaults.OnTappingOutsideOfPopup, popupOptions.OnTappingOutsideOfPopup); + Assert.Null(popupOptions.OnTappingOutsideOfPopup); } [Fact] @@ -78,7 +82,9 @@ public void OnTappingOutsideOfPopup_SetValue_ShouldBeUpdated() public void Shape_DefaultValue_ShouldBeNull() { var popupOptions = new PopupOptions(); - Assert.Equal(PopupOptionsDefaults.Shape, popupOptions.Shape); + Assert.Equal(new CornerRadius(20, 20, 20, 20), ((RoundRectangle?)popupOptions.Shape)?.CornerRadius); + Assert.Equal(2, ((RoundRectangle?)popupOptions.Shape)?.StrokeThickness); + Assert.Equal(Colors.LightGray, ((RoundRectangle?)popupOptions.Shape)?.Stroke); } [Fact] diff --git a/src/CommunityToolkit.Maui.UnitTests/Views/Popup/PopupPageTests.cs b/src/CommunityToolkit.Maui.UnitTests/Views/Popup/PopupPageTests.cs index b6c6a4bb68..d267eceebc 100644 --- a/src/CommunityToolkit.Maui.UnitTests/Views/Popup/PopupPageTests.cs +++ b/src/CommunityToolkit.Maui.UnitTests/Views/Popup/PopupPageTests.cs @@ -29,22 +29,6 @@ public void Constructor_ShouldThrowArgumentNullException_WhenPopupIsNull() act.Should().Throw(); } - [Fact] - public void Constructor_ShouldThrowArgumentNullException_WhenPopupOptionsIsNull() - { - // Arrange - var view = new ContentView(); - var taskCompletionSource = new TaskCompletionSource(); - - // Act -#pragma warning disable CS8625 // Cannot convert null literal to non-nullable reference type. - Action act = () => new PopupPage(view, null); -#pragma warning restore CS8625 // Cannot convert null literal to non-nullable reference type. - - // Assert - act.Should().Throw(); - } - [Fact] public async Task Close_ShouldThrowInvalidOperationException_NoPopupPageFound() { @@ -129,21 +113,6 @@ public void PopupPageT_Constructor_ShouldThrowArgumentNullException_WhenPopupIsN act.Should().Throw(); } - [Fact] - public void PopupPageT_Constructor_ShouldThrowArgumentNullException_WhenPopupOptionsIsNull() - { - // Arrange - var view = new ContentView(); - - // Act -#pragma warning disable CS8625 // Cannot convert null literal to non-nullable reference type. - Action act = () => new PopupPage(view, null); -#pragma warning restore CS8625 // Cannot convert null literal to non-nullable reference type. - - // Assert - act.Should().Throw(); - } - [Fact] public async Task PopupPageT_Close_ShouldSetResultAndPopModalAsync() { @@ -226,7 +195,7 @@ public void TapGestureRecognizer_VerifyCanBeDismissedByTappingOutsideOfPopup_Sho // Act var popupPage = new PopupPage(view, popupOptions); - var tapGestureRecognizer = popupPage.Content.Children.OfType().Single().GestureRecognizers.OfType().Single(); + var tapGestureRecognizer = GetTapOutsideGestureRecognizer(popupPage); // Assert Assert.True(tapGestureRecognizer.Command?.CanExecute(null)); @@ -337,17 +306,6 @@ public void Constructor_WithNullPopup_ThrowsArgumentNullException() #pragma warning restore CS8625 // Cannot convert null literal to non-nullable reference type. } - [Fact] - public void Constructor_WithNullPopupOptions_ThrowsArgumentNullException() - { - // Arrange - var view = new Label(); - IPopupOptions popupOptions = null!; - - // Act & Assert - Assert.Throws(() => new PopupPage(view, popupOptions)); - } - [Fact] public async Task TapGestureRecognizer_ShouldClosePopupWhenCanBeDismissedIsTrue() { diff --git a/src/CommunityToolkit.Maui.UnitTests/Views/Popup/PopupTests.cs b/src/CommunityToolkit.Maui.UnitTests/Views/Popup/PopupTests.cs index 71b28b8b4b..e99ab57465 100644 --- a/src/CommunityToolkit.Maui.UnitTests/Views/Popup/PopupTests.cs +++ b/src/CommunityToolkit.Maui.UnitTests/Views/Popup/PopupTests.cs @@ -12,21 +12,22 @@ public class PopupTests : BaseViewTest [Fact] public void PopupBackgroundColor_DefaultValue_ShouldBeWhite() { - Assert.Equal(PopupDefaults.BackgroundColor, Colors.White); + var popup = new Popup(); + Assert.Equal(Colors.White, popup.BackgroundColor); } [Fact] public void CanBeDismissedByTappingOutsideOfPopup_DefaultValue_ShouldBeTrue() { var popup = new Popup(); - Assert.Equal(PopupDefaults.CanBeDismissedByTappingOutsideOfPopup, popup.CanBeDismissedByTappingOutsideOfPopup); + Assert.True(popup.CanBeDismissedByTappingOutsideOfPopup); } [Fact] public void Margin_DefaultValue_ShouldBeDefaultThickness() { var popup = new Popup(); - Assert.Equal(PopupDefaults.Margin, popup.Margin); + Assert.Equal(new Thickness(30), popup.Margin); } [Fact] @@ -42,7 +43,7 @@ public void Margin_SetValue_ShouldBeUpdated() public void Padding_DefaultValue_ShouldBeDefaultThickness() { var popup = new Popup(); - Assert.Equal(PopupDefaults.Padding, popup.Padding); + Assert.Equal(new Thickness(15), popup.Padding); } [Fact] @@ -58,7 +59,7 @@ public void Padding_SetValue_ShouldBeUpdated() public void VerticalOptions_DefaultValue_ShouldBeDefaultLayoutOptions() { var popup = new Popup(); - Assert.Equal(PopupDefaults.VerticalOptions, popup.VerticalOptions); + Assert.Equal(LayoutOptions.Center, popup.VerticalOptions); } [Fact] @@ -74,7 +75,7 @@ public void VerticalOptions_SetValue_ShouldBeUpdated() public void HorizontalOptions_DefaultValue_ShouldBeDefaultLayoutOptions() { var popup = new Popup(); - Assert.Equal(PopupDefaults.HorizontalOptions, popup.HorizontalOptions); + Assert.Equal(LayoutOptions.Center, popup.HorizontalOptions); } [Fact] diff --git a/src/CommunityToolkit.Maui/Extensions/PopupExtensions.shared.cs b/src/CommunityToolkit.Maui/Extensions/PopupExtensions.shared.cs index 752946f673..a2b512e097 100644 --- a/src/CommunityToolkit.Maui/Extensions/PopupExtensions.shared.cs +++ b/src/CommunityToolkit.Maui/Extensions/PopupExtensions.shared.cs @@ -34,7 +34,7 @@ public static async void ShowPopup(this INavigation navigation, View view, IPopu ArgumentNullException.ThrowIfNull(navigation); ArgumentNullException.ThrowIfNull(view); - var popupPage = new PopupPage(view, options ?? PopupOptions.Empty); + var popupPage = new PopupPage(view, options); await navigation.PushModalAsync(popupPage, false); } @@ -133,7 +133,7 @@ public static async Task ShowPopupAsync(this INavigation navigatio TaskCompletionSource taskCompletionSource = new(TaskCreationOptions.RunContinuationsAsynchronously); - var popupPage = new PopupPage(view, options ?? PopupOptions.Empty); + var popupPage = new PopupPage(view, options); popupPage.PopupClosed += HandlePopupClosed; await navigation.PushModalAsync(popupPage, false).WaitAsync(token); @@ -166,7 +166,7 @@ public static async Task ShowPopupAsync(this Shell shell, View vie TaskCompletionSource taskCompletionSource = new(TaskCreationOptions.RunContinuationsAsynchronously); - var popupPage = new PopupPage(view, options ?? PopupOptions.Empty); + var popupPage = new PopupPage(view, options); popupPage.PopupClosed += HandlePopupClosed; Routing.RegisterRoute(popupPageRoute, new PopupPageRouteFactory(popupPage)); diff --git a/src/CommunityToolkit.Maui/Options.cs b/src/CommunityToolkit.Maui/Options.cs index 9c70dfa891..0d854e8a72 100644 --- a/src/CommunityToolkit.Maui/Options.cs +++ b/src/CommunityToolkit.Maui/Options.cs @@ -1,5 +1,7 @@ using CommunityToolkit.Maui.Behaviors; using CommunityToolkit.Maui.Converters; +using CommunityToolkit.Maui.Views; +using CommunityToolkit.Maui.Extensions; #if WINDOWS using Microsoft.Maui.LifecycleEvents; #endif @@ -9,7 +11,12 @@ namespace CommunityToolkit.Maui; /// /// .NET MAUI Community Toolkit Options. /// +#if NET9_0 public class Options() : Core.Options +#elif NET10_OR_GREATER +#error Remove .NET 9 code now that we're upgrading to .NET 10; Options should not have a public constructor, only public methods. Having a public constructor allows developers to override the values set when using AppBuilderExtensions +public class Options : Core.Options +#endif { readonly MauiAppBuilder? builder; @@ -22,6 +29,8 @@ internal Options(in MauiAppBuilder builder) : this() internal static bool ShouldSuppressExceptionsInConverters { get; private set; } internal static bool ShouldSuppressExceptionsInBehaviors { get; private set; } internal static bool ShouldEnableSnackbarOnWindows { get; private set; } + internal static DefaultPopupSettings DefaultPopupSettings { get; private set; } = new(); + internal static DefaultPopupOptionsSettings DefaultPopupOptionsSettings { get; private set; } = new(); /// /// Will return the default value instead of throwing an exception when using . @@ -105,4 +114,24 @@ static void OnSnackbarNotificationInvoked(Microsoft.Windows.AppNotifications.App ShouldEnableSnackbarOnWindows = value; } + + /// + /// Sets the default settings for + /// + /// + /// The settings passed in here will be set on initialization of every new Popup + public void SetPopupDefaults(DefaultPopupSettings globalPopupSettings) + { + DefaultPopupSettings = globalPopupSettings; + } + + /// + /// Sets the default settings for + /// + /// + /// The settings passed in here will be used when is called the parameter is null + public void SetPopupOptionsDefaults(DefaultPopupOptionsSettings globalPopupOptionsSettings) + { + DefaultPopupOptionsSettings = globalPopupOptionsSettings; + } } \ No newline at end of file diff --git a/src/CommunityToolkit.Maui/Primitives/DefaultPopupOptionsSettings.cs b/src/CommunityToolkit.Maui/Primitives/DefaultPopupOptionsSettings.cs new file mode 100644 index 0000000000..d2de58bc0d --- /dev/null +++ b/src/CommunityToolkit.Maui/Primitives/DefaultPopupOptionsSettings.cs @@ -0,0 +1,77 @@ +using Microsoft.Maui.Controls.Shapes; + +namespace CommunityToolkit.Maui; + +/// +/// Sets global app defaults for +/// +/// Set in .UseMauiCommunityToolkit(Options) +public record DefaultPopupOptionsSettings : IPopupOptions +{ + /// + public bool CanBeDismissedByTappingOutsideOfPopup { get; init; }= PopupOptionsDefaults.CanBeDismissedByTappingOutsideOfPopup; + + /// + public Action? OnTappingOutsideOfPopup { get; init;} = PopupOptionsDefaults.OnTappingOutsideOfPopup; + + /// + public Color PageOverlayColor { get;init; } = PopupOptionsDefaults.PageOverlayColor; + + /// + public Shape? Shape { get; init; } = PopupOptionsDefaults.Shape; + + /// + public Shadow? Shadow { get; init; } = PopupOptionsDefaults.Shadow; + + /// + /// Default Values for + /// + static class PopupOptionsDefaults + { + /// + /// Default value for + /// + public const double BorderStrokeThickness = 2; + + /// + /// Default value for + /// + public const bool CanBeDismissedByTappingOutsideOfPopup = true; + + /// + /// Default value for + /// + public static Action? OnTappingOutsideOfPopup { get; } = null; + + /// + /// Default value for + /// + public static Color PageOverlayColor { get; } = Colors.Black.WithAlpha(0.3f); + + /// + /// Default value for + /// + public static Brush? BorderStroke { get; } = Colors.LightGray; + + /// + /// Default value for + /// + public static Shape? Shape { get; } = new RoundRectangle + { + CornerRadius = new CornerRadius(20, 20, 20, 20), + StrokeThickness = BorderStrokeThickness, + Stroke = BorderStroke + }; + + /// + /// Default value for + /// + public static Shadow Shadow { get; } = new Shadow + { + Brush = Colors.Black, + Offset = new(20, 20), + Radius = 40, + Opacity = 0.8f + }; + } +} \ No newline at end of file diff --git a/src/CommunityToolkit.Maui/Primitives/DefaultPopupSettings.cs b/src/CommunityToolkit.Maui/Primitives/DefaultPopupSettings.cs new file mode 100644 index 0000000000..96bce53951 --- /dev/null +++ b/src/CommunityToolkit.Maui/Primitives/DefaultPopupSettings.cs @@ -0,0 +1,64 @@ +using CommunityToolkit.Maui.Views; + +namespace CommunityToolkit.Maui; + +/// +/// Sets global app defaults for +/// +/// Set in .UseMauiCommunityToolkit(Options) +public record DefaultPopupSettings +{ + /// + public Thickness Margin { get; init; } = PopupDefaults.Margin; + + /// + public Thickness Padding { get; init; } = PopupDefaults.Padding; + + /// + public LayoutOptions HorizontalOptions { get; init; } = PopupDefaults.HorizontalOptions; + + /// + public LayoutOptions VerticalOptions { get; init; } = PopupDefaults.VerticalOptions; + + /// + public bool CanBeDismissedByTappingOutsideOfPopup { get; init; } = PopupDefaults.CanBeDismissedByTappingOutsideOfPopup; + + /// + public Color BackgroundColor { get; init; } = PopupDefaults.BackgroundColor; + + /// + /// Default Values for + /// + static class PopupDefaults + { + /// + /// Default value for + /// + public static Thickness Margin { get; } = new(30); + + /// + /// Default value for + /// + public static Thickness Padding { get; } = new(15); + + /// + /// Default value for + /// + public static LayoutOptions VerticalOptions { get; } = LayoutOptions.Center; + + /// + /// Default value for + /// + public static LayoutOptions HorizontalOptions { get; } = LayoutOptions.Center; + + /// + /// Default value for BackgroundColor + /// + public static Color BackgroundColor { get; } = Colors.White; + + /// + /// Default value for + /// + public const bool CanBeDismissedByTappingOutsideOfPopup = true; + } +} \ No newline at end of file diff --git a/src/CommunityToolkit.Maui/Primitives/Defaults/PopupDefaults.shared.cs b/src/CommunityToolkit.Maui/Primitives/Defaults/PopupDefaults.shared.cs deleted file mode 100644 index 79a5e6dde3..0000000000 --- a/src/CommunityToolkit.Maui/Primitives/Defaults/PopupDefaults.shared.cs +++ /dev/null @@ -1,39 +0,0 @@ -using CommunityToolkit.Maui.Views; - -namespace CommunityToolkit.Maui; - -/// -/// Default Values for -/// -static class PopupDefaults -{ - /// - /// Default value for - /// - public static Thickness Margin { get; } = new(30); - - /// - /// Default value for - /// - public static Thickness Padding { get; } = new(15); - - /// - /// Default value for - /// - public static LayoutOptions VerticalOptions { get; } = LayoutOptions.Center; - - /// - /// Default value for - /// - public static LayoutOptions HorizontalOptions { get; } = LayoutOptions.Center; - - /// - /// Default value for BackgroundColor - /// - public static Color BackgroundColor { get; } = Colors.White; - - /// - /// Default value for - /// - public const bool CanBeDismissedByTappingOutsideOfPopup = PopupOptionsDefaults.CanBeDismissedByTappingOutsideOfPopup; -} \ No newline at end of file diff --git a/src/CommunityToolkit.Maui/Primitives/Defaults/PopupOptionsDefaults.shared.cs b/src/CommunityToolkit.Maui/Primitives/Defaults/PopupOptionsDefaults.shared.cs deleted file mode 100644 index 901741015d..0000000000 --- a/src/CommunityToolkit.Maui/Primitives/Defaults/PopupOptionsDefaults.shared.cs +++ /dev/null @@ -1,57 +0,0 @@ -using System.ComponentModel; -using CommunityToolkit.Maui.Views; -using Microsoft.Maui.Controls.Shapes; - -namespace CommunityToolkit.Maui; - -/// -/// Default Values for -/// -static class PopupOptionsDefaults -{ - /// - /// Default value for - /// - public const double BorderStrokeThickness = 2; - - /// - /// Default value for - /// - public const bool CanBeDismissedByTappingOutsideOfPopup = true; - - /// - /// Default value for - /// - public static Action? OnTappingOutsideOfPopup { get; } = null; - - /// - /// Default value for - /// - public static Color PageOverlayColor { get; } = Colors.Black.WithAlpha(0.3f); - - /// - /// Default value for - /// - public static Brush? BorderStroke { get; } = Colors.LightGray; - - /// - /// Default value for - /// - public static IShape? Shape { get; } = new RoundRectangle - { - CornerRadius = new CornerRadius(20, 20, 20, 20), - StrokeThickness = BorderStrokeThickness, - Stroke = BorderStroke - }; - - /// - /// Default value for - /// - public static Shadow Shadow { get; } = new Shadow - { - Brush = Colors.Black, - Offset = new(20, 20), - Radius = 40, - Opacity = 0.8f - }; -} \ No newline at end of file diff --git a/src/CommunityToolkit.Maui/Views/Popup/Popup.shared.cs b/src/CommunityToolkit.Maui/Views/Popup/Popup.shared.cs index c971c92294..688a326889 100644 --- a/src/CommunityToolkit.Maui/Views/Popup/Popup.shared.cs +++ b/src/CommunityToolkit.Maui/Views/Popup/Popup.shared.cs @@ -30,17 +30,18 @@ public partial class Popup : ContentView /// /// Backing BindableProperty for the property. /// - public static readonly BindableProperty CanBeDismissedByTappingOutsideOfPopupProperty = BindableProperty.Create(nameof(CanBeDismissedByTappingOutsideOfPopup), typeof(bool), typeof(Popup), PopupDefaults.CanBeDismissedByTappingOutsideOfPopup); + public static readonly BindableProperty CanBeDismissedByTappingOutsideOfPopupProperty = BindableProperty.Create(nameof(CanBeDismissedByTappingOutsideOfPopup), typeof(bool), typeof(Popup), Options.DefaultPopupSettings.CanBeDismissedByTappingOutsideOfPopup); /// /// Initializes Popup /// public Popup() { - Margin = PopupDefaults.Margin; - Padding = PopupDefaults.Padding; - HorizontalOptions = PopupDefaults.HorizontalOptions; - VerticalOptions = PopupDefaults.VerticalOptions; + Margin = Options.DefaultPopupSettings.Margin; + Padding = Options.DefaultPopupSettings.Padding; + HorizontalOptions = Options.DefaultPopupSettings.HorizontalOptions; + VerticalOptions = Options.DefaultPopupSettings.VerticalOptions; + BackgroundColor = Options.DefaultPopupSettings.BackgroundColor; } /// diff --git a/src/CommunityToolkit.Maui/Views/Popup/PopupOptions.shared.cs b/src/CommunityToolkit.Maui/Views/Popup/PopupOptions.shared.cs index baba013955..285bf517fe 100644 --- a/src/CommunityToolkit.Maui/Views/Popup/PopupOptions.shared.cs +++ b/src/CommunityToolkit.Maui/Views/Popup/PopupOptions.shared.cs @@ -10,27 +10,27 @@ public partial class PopupOptions : BindableObject, IPopupOptions /// /// Backing BindableProperty for the property. /// - public static readonly BindableProperty CanBeDismissedByTappingOutsideOfPopupProperty = BindableProperty.Create(nameof(CanBeDismissedByTappingOutsideOfPopup), typeof(bool), typeof(PopupOptions), PopupOptionsDefaults.CanBeDismissedByTappingOutsideOfPopup); + public static readonly BindableProperty CanBeDismissedByTappingOutsideOfPopupProperty = BindableProperty.Create(nameof(CanBeDismissedByTappingOutsideOfPopup), typeof(bool), typeof(PopupOptions), Options.DefaultPopupOptionsSettings.CanBeDismissedByTappingOutsideOfPopup); /// /// Backing BindableProperty for the property. /// - public static readonly BindableProperty OnTappingOutsideOfPopupProperty = BindableProperty.Create(nameof(OnTappingOutsideOfPopup), typeof(Action), typeof(PopupOptions), PopupOptionsDefaults.OnTappingOutsideOfPopup); + public static readonly BindableProperty OnTappingOutsideOfPopupProperty = BindableProperty.Create(nameof(OnTappingOutsideOfPopup), typeof(Action), typeof(PopupOptions), Options.DefaultPopupOptionsSettings.OnTappingOutsideOfPopup); /// /// Backing BindableProperty for the property. /// - public static readonly BindableProperty PageOverlayColorProperty = BindableProperty.Create(nameof(PageOverlayColor), typeof(Color), typeof(PopupOptions), PopupOptionsDefaults.PageOverlayColor); + public static readonly BindableProperty PageOverlayColorProperty = BindableProperty.Create(nameof(PageOverlayColor), typeof(Color), typeof(PopupOptions), Options.DefaultPopupOptionsSettings.PageOverlayColor); /// /// Backing BindableProperty for the property. /// - public static readonly BindableProperty ShapeProperty = BindableProperty.Create(nameof(Shape), typeof(Shape), typeof(PopupOptions), PopupOptionsDefaults.Shape); + public static readonly BindableProperty ShapeProperty = BindableProperty.Create(nameof(Shape), typeof(Shape), typeof(PopupOptions), Options.DefaultPopupOptionsSettings.Shape); /// /// Backing BindableProperty for the property. /// - public static readonly BindableProperty ShadowProperty = BindableProperty.Create(nameof(Shadow), typeof(Shadow), typeof(PopupOptions), PopupOptionsDefaults.Shadow); + public static readonly BindableProperty ShadowProperty = BindableProperty.Create(nameof(Shadow), typeof(Shadow), typeof(PopupOptions), Options.DefaultPopupOptionsSettings.Shadow); /// /// An empty instance of containing default values. diff --git a/src/CommunityToolkit.Maui/Views/Popup/PopupPage.shared.cs b/src/CommunityToolkit.Maui/Views/Popup/PopupPage.shared.cs index ebf1a76e75..b089bc0f8d 100644 --- a/src/CommunityToolkit.Maui/Views/Popup/PopupPage.shared.cs +++ b/src/CommunityToolkit.Maui/Views/Popup/PopupPage.shared.cs @@ -10,10 +10,10 @@ namespace CommunityToolkit.Maui.Views; -sealed partial class PopupPage(Popup popup, IPopupOptions popupOptions) +sealed partial class PopupPage(Popup popup, IPopupOptions? popupOptions) : PopupPage(popup, popupOptions) { - public PopupPage(View view, IPopupOptions popupOptions) + public PopupPage(View view, IPopupOptions? popupOptions) : this(view as Popup ?? CreatePopupFromView>(view), popupOptions) { } @@ -27,19 +27,18 @@ partial class PopupPage : ContentPage, IQueryAttributable readonly IPopupOptions popupOptions; readonly Command tapOutsideOfPopupCommand; - public PopupPage(View view, IPopupOptions popupOptions) + public PopupPage(View view, IPopupOptions? popupOptions) : this(view as Popup ?? CreatePopupFromView(view), popupOptions) { ArgumentNullException.ThrowIfNull(view); } - public PopupPage(Popup popup, IPopupOptions popupOptions) + public PopupPage(Popup popup, IPopupOptions? popupOptions) { ArgumentNullException.ThrowIfNull(popup); - ArgumentNullException.ThrowIfNull(popupOptions); - + this.popup = popup; - this.popupOptions = popupOptions; + this.popupOptions = popupOptions ??= Options.DefaultPopupOptionsSettings; tapOutsideOfPopupCommand = new Command(async () => { @@ -130,15 +129,14 @@ protected override void OnNavigatedTo(NavigatedToEventArgs args) var popup = new T { - BackgroundColor = view.BackgroundColor ??= PopupDefaults.BackgroundColor, Content = view }; popup.SetBinding(BackgroundProperty, static (View view) => view.Background, source: view, mode: BindingMode.OneWay); popup.SetBinding(BindingContextProperty, static (View view) => view.BindingContext, source: view, mode: BindingMode.OneWay); popup.SetBinding(BackgroundColorProperty, static (View view) => view.BackgroundColor, source: view, mode: BindingMode.OneWay); - popup.SetBinding(Popup.MarginProperty, static (View view) => view.Margin, source: view, mode: BindingMode.OneWay); - popup.SetBinding(Popup.VerticalOptionsProperty, static (View view) => view.VerticalOptions, source: view, mode: BindingMode.OneWay, converter: new VerticalOptionsConverter()); - popup.SetBinding(Popup.HorizontalOptionsProperty, static (View view) => view.HorizontalOptions, source: view, mode: BindingMode.OneWay, converter: new HorizontalOptionsConverter()); + popup.SetBinding(Popup.MarginProperty, static (View view) => view.Margin, source: view, mode: BindingMode.OneWay, converter: new MarginConverter()); + popup.SetBinding(Popup.VerticalOptionsProperty, static (View view) => view.VerticalOptions, source: view, mode: BindingMode.OneWay); + popup.SetBinding(Popup.HorizontalOptionsProperty, static (View view) => view.HorizontalOptions, source: view, mode: BindingMode.OneWay); if (view is IPaddingElement paddingElement) { @@ -198,23 +196,22 @@ public PopupPageLayout(in Popup popupContent, in IPopupOptions options, in IComm PopupBorder = new Border { - BackgroundColor = popupContent.BackgroundColor ??= PopupDefaults.BackgroundColor, + BackgroundColor = popupContent.BackgroundColor ??= Options.DefaultPopupSettings.BackgroundColor, Content = popupContent }; // Bind `Popup` values through to Border using OneWay Bindings - PopupBorder.SetBinding(Border.MarginProperty, static (Popup popup) => popup.Margin, source: popupContent, mode: BindingMode.OneWay); - PopupBorder.SetBinding(Border.PaddingProperty, static (Popup popup) => popup.Padding, source: popupContent, mode: BindingMode.OneWay); + PopupBorder.SetBinding(Border.MarginProperty, static (Popup popup) => popup.Margin, source: popupContent, mode: BindingMode.OneWay, converter: new MarginConverter()); PopupBorder.SetBinding(Border.BackgroundProperty, static (Popup popup) => popup.Background, source: popupContent, mode: BindingMode.OneWay); - PopupBorder.SetBinding(Border.BackgroundColorProperty, static (Popup popup) => popup.BackgroundColor, source: popupContent, mode: BindingMode.OneWay); - PopupBorder.SetBinding(Border.VerticalOptionsProperty, static (Popup popup) => popup.VerticalOptions, source: popupContent, mode: BindingMode.OneWay); - PopupBorder.SetBinding(Border.HorizontalOptionsProperty, static (Popup popup) => popup.HorizontalOptions, source: popupContent, mode: BindingMode.OneWay); + PopupBorder.SetBinding(Border.BackgroundColorProperty, static (Popup popup) => popup.BackgroundColor, source: popupContent, mode: BindingMode.OneWay, converter: new BackgroundColorConverter()); + PopupBorder.SetBinding(Border.VerticalOptionsProperty, static (Popup popup) => popup.VerticalOptions, source: popupContent, mode: BindingMode.OneWay, converter: new VerticalOptionsConverter()); + PopupBorder.SetBinding(Border.HorizontalOptionsProperty, static (Popup popup) => popup.HorizontalOptions, source: popupContent, mode: BindingMode.OneWay, converter: new HorizontalOptionsConverter()); // Bind `PopupOptions` values through to Border using OneWay Bindings PopupBorder.SetBinding(Border.ShadowProperty, static (IPopupOptions options) => options.Shadow, source: options, mode: BindingMode.OneWay); - PopupBorder.SetBinding(Border.StrokeProperty, static (IPopupOptions options) => options.Shape, source: options, converter: new BorderStrokeConverter(), mode: BindingMode.OneWay); + PopupBorder.SetBinding(Border.StrokeProperty, static (IPopupOptions options) => options.Shape, source: options, mode: BindingMode.OneWay, converter: new BorderStrokeConverter()); PopupBorder.SetBinding(Border.StrokeShapeProperty, static (IPopupOptions options) => options.Shape, source: options, mode: BindingMode.OneWay); - PopupBorder.SetBinding(Border.StrokeThicknessProperty, static (IPopupOptions options) => options.Shape, source: options, converter: new BorderStrokeThicknessConverter(), mode: BindingMode.OneWay); + PopupBorder.SetBinding(Border.StrokeThicknessProperty, static (IPopupOptions options) => options.Shape, source: options, mode: BindingMode.OneWay, converter: new BorderStrokeThicknessConverter()); Children.Add(PopupBorder); } @@ -223,37 +220,51 @@ public PopupPageLayout(in Popup popupContent, in IPopupOptions options, in IComm sealed partial class BorderStrokeThicknessConverter : BaseConverterOneWay { - public override double DefaultConvertReturnValue { get; set; } = PopupOptionsDefaults.BorderStrokeThickness; + public override double DefaultConvertReturnValue { get; set; } = Options.DefaultPopupOptionsSettings.Shape?.StrokeThickness ?? 0; public override double ConvertFrom(Shape? value, CultureInfo? culture) => value?.StrokeThickness ?? 0; } sealed partial class BorderStrokeConverter : BaseConverterOneWay { - public override Brush? DefaultConvertReturnValue { get; set; } = PopupOptionsDefaults.BorderStroke; + public override Brush? DefaultConvertReturnValue { get; set; } = Options.DefaultPopupOptionsSettings.Shape?.Stroke; public override Brush? ConvertFrom(Shape? value, CultureInfo? culture) => value?.Stroke; } - } + + sealed partial class HorizontalOptionsConverter : BaseConverterOneWay + { + public override LayoutOptions DefaultConvertReturnValue { get; set; } = Options.DefaultPopupSettings.HorizontalOptions; - sealed partial class PaddingConverter : BaseConverterOneWay - { - public override Thickness DefaultConvertReturnValue { get; set; } = PopupDefaults.Padding; + public override LayoutOptions ConvertFrom(LayoutOptions value, CultureInfo? culture) => value == LayoutOptions.Fill ? Options.DefaultPopupSettings.HorizontalOptions : value; + } + + sealed partial class VerticalOptionsConverter : BaseConverterOneWay + { + public override LayoutOptions DefaultConvertReturnValue { get; set; } = Options.DefaultPopupSettings.VerticalOptions; + + public override LayoutOptions ConvertFrom(LayoutOptions value, CultureInfo? culture) => value == LayoutOptions.Fill ? Options.DefaultPopupSettings.VerticalOptions : value; + } + + sealed partial class BackgroundColorConverter : BaseConverterOneWay + { + public override Color DefaultConvertReturnValue { get; set; } = Options.DefaultPopupSettings.BackgroundColor; - public override Thickness ConvertFrom(Thickness value, CultureInfo? culture) => value == default ? PopupDefaults.Padding : value; + public override Color ConvertFrom(Color? value, CultureInfo? culture) => value ?? Options.DefaultPopupSettings.BackgroundColor; + } } - sealed partial class HorizontalOptionsConverter : BaseConverterOneWay + sealed partial class PaddingConverter : BaseConverterOneWay { - public override LayoutOptions DefaultConvertReturnValue { get; set; } = PopupDefaults.HorizontalOptions; + public override Thickness DefaultConvertReturnValue { get; set; } = Options.DefaultPopupSettings.Padding; - public override LayoutOptions ConvertFrom(LayoutOptions value, CultureInfo? culture) => value == LayoutOptions.Fill ? PopupDefaults.HorizontalOptions : value; + public override Thickness ConvertFrom(Thickness value, CultureInfo? culture) => value == default ? Options.DefaultPopupSettings.Padding : value; } - - sealed partial class VerticalOptionsConverter : BaseConverterOneWay + + sealed partial class MarginConverter : BaseConverterOneWay { - public override LayoutOptions DefaultConvertReturnValue { get; set; } = PopupDefaults.VerticalOptions; + public override Thickness DefaultConvertReturnValue { get; set; } = Options.DefaultPopupSettings.Margin; - public override LayoutOptions ConvertFrom(LayoutOptions value, CultureInfo? culture) => value == LayoutOptions.Fill ? PopupDefaults.VerticalOptions : value; + public override Thickness ConvertFrom(Thickness value, CultureInfo? culture) => value == default ? Options.DefaultPopupSettings.Margin : value; } } \ No newline at end of file