From 336e1e7a58923447274dd924c2056bcbc21fcf9b Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Wed, 8 Oct 2025 19:23:28 +0000 Subject: [PATCH 1/3] Initial plan From 0cfd648c600b310fd672faf6af4f2676cf2cbc9c Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Wed, 8 Oct 2025 19:37:28 +0000 Subject: [PATCH 2/3] Add VSM for SafeAreaEdges based on orientation to Page template styles Co-authored-by: PureWeen <5375137+PureWeen@users.noreply.github.com> --- .../SafeAreaOrientationTests.cs | 159 ++++++++++++++++++ .../maui-mobile/Resources/Styles/Styles.xaml | 22 +++ .../MauiApp.1/Resources/Styles/Styles.xaml | 22 +++ 3 files changed, 203 insertions(+) create mode 100644 src/Controls/tests/Core.UnitTests/SafeAreaOrientationTests.cs diff --git a/src/Controls/tests/Core.UnitTests/SafeAreaOrientationTests.cs b/src/Controls/tests/Core.UnitTests/SafeAreaOrientationTests.cs new file mode 100644 index 000000000000..6a5028ccaa78 --- /dev/null +++ b/src/Controls/tests/Core.UnitTests/SafeAreaOrientationTests.cs @@ -0,0 +1,159 @@ +using Microsoft.Maui.Devices; +using Microsoft.Maui.Graphics; +using Xunit; + +namespace Microsoft.Maui.Controls.Core.UnitTests; + +public class SafeAreaOrientationTests : BaseTestFixture +{ + [Fact] + public void SafeAreaEdgesCanBeSetViaVisualStateManager() + { + DeviceDisplay.SetCurrent(new MockDeviceDisplay( + new(100, 200, 2, DisplayOrientation.Portrait, DisplayRotation.Rotation0))); + + var page = new ContentPage(); + + VisualStateManager.SetVisualStateGroups(page, new VisualStateGroupList + { + new VisualStateGroup + { + Name = "OrientationStates", + States = + { + new VisualState + { + Name = "Portrait", + StateTriggers = { new OrientationStateTrigger { Orientation = DisplayOrientation.Portrait } }, + Setters = { new Setter { Property = ContentPage.SafeAreaEdgesProperty, Value = SafeAreaEdges.All } } + }, + new VisualState + { + Name = "Landscape", + StateTriggers = { new OrientationStateTrigger { Orientation = DisplayOrientation.Landscape } }, + Setters = { new Setter { Property = ContentPage.SafeAreaEdgesProperty, Value = new SafeAreaEdges(SafeAreaRegions.All, SafeAreaRegions.None) } } + } + } + } + }); + + var window = new Window(page); + + // Initially in portrait, should have All edges + Assert.Equal(SafeAreaEdges.All, page.SafeAreaEdges); + } + + [Fact] + public void SafeAreaEdgesChangesWhenOrientationChanges() + { + var mockDeviceDisplay = new MockDeviceDisplay( + new(100, 200, 2, DisplayOrientation.Portrait, DisplayRotation.Rotation0)); + DeviceDisplay.SetCurrent(mockDeviceDisplay); + + var page = new ContentPage(); + + VisualStateManager.SetVisualStateGroups(page, new VisualStateGroupList + { + new VisualStateGroup + { + Name = "OrientationStates", + States = + { + new VisualState + { + Name = "Portrait", + StateTriggers = { new OrientationStateTrigger { Orientation = DisplayOrientation.Portrait } }, + Setters = { new Setter { Property = ContentPage.SafeAreaEdgesProperty, Value = SafeAreaEdges.All } } + }, + new VisualState + { + Name = "Landscape", + StateTriggers = { new OrientationStateTrigger { Orientation = DisplayOrientation.Landscape } }, + Setters = { new Setter { Property = ContentPage.SafeAreaEdgesProperty, Value = new SafeAreaEdges(SafeAreaRegions.All, SafeAreaRegions.None) } } + } + } + } + }); + + var window = new Window(page); + + // Initially in portrait + Assert.Equal(SafeAreaEdges.All, page.SafeAreaEdges); + + // Change to landscape + mockDeviceDisplay.SetMainDisplayOrientation(DisplayOrientation.Landscape); + + // Should now use different safe area edges (horizontal All, vertical None) + var expected = new SafeAreaEdges(SafeAreaRegions.All, SafeAreaRegions.None); + Assert.Equal(expected, page.SafeAreaEdges); + + window.Page = null; // Cleanup + } + + [Fact] + public void SafeAreaEdgesCanUseContainerRegion() + { + DeviceDisplay.SetCurrent(new MockDeviceDisplay( + new(100, 200, 2, DisplayOrientation.Portrait, DisplayRotation.Rotation0))); + + var page = new ContentPage(); + + VisualStateManager.SetVisualStateGroups(page, new VisualStateGroupList + { + new VisualStateGroup + { + Name = "OrientationStates", + States = + { + new VisualState + { + Name = "Portrait", + StateTriggers = { new OrientationStateTrigger { Orientation = DisplayOrientation.Portrait } }, + Setters = { new Setter { Property = ContentPage.SafeAreaEdgesProperty, Value = SafeAreaEdges.Container } } + } + } + } + }); + + var window = new Window(page); + + // Should use Container region + Assert.Equal(SafeAreaEdges.Container, page.SafeAreaEdges); + + window.Page = null; // Cleanup + } + + [Fact] + public void SafeAreaEdgesWorksWithDifferentPageTypes() + { + DeviceDisplay.SetCurrent(new MockDeviceDisplay( + new(100, 200, 2, DisplayOrientation.Portrait, DisplayRotation.Rotation0))); + + var contentPage = new ContentPage(); + var tabbedPage = new TabbedPage(); + + VisualStateManager.SetVisualStateGroups(contentPage, new VisualStateGroupList + { + new VisualStateGroup + { + Name = "OrientationStates", + States = + { + new VisualState + { + Name = "Portrait", + StateTriggers = { new OrientationStateTrigger { Orientation = DisplayOrientation.Portrait } }, + Setters = { new Setter { Property = ContentPage.SafeAreaEdgesProperty, Value = SafeAreaEdges.All } } + } + } + } + }); + + var window = new Window(contentPage); + + // ContentPage should respect VSM + Assert.Equal(SafeAreaEdges.All, contentPage.SafeAreaEdges); + + window.Page = null; // Cleanup + } +} diff --git a/src/Templates/src/templates/maui-mobile/Resources/Styles/Styles.xaml b/src/Templates/src/templates/maui-mobile/Resources/Styles/Styles.xaml index 2a9a28bfca2a..c94b8cb72e39 100644 --- a/src/Templates/src/templates/maui-mobile/Resources/Styles/Styles.xaml +++ b/src/Templates/src/templates/maui-mobile/Resources/Styles/Styles.xaml @@ -443,6 +443,28 @@ + + + + + + + + + + + + + + + + + + + + + +