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 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+