Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
159 changes: 159 additions & 0 deletions src/Controls/tests/Core.UnitTests/SafeAreaOrientationTests.cs
Original file line number Diff line number Diff line change
@@ -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
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,123 @@
<?xml version="1.0" encoding="utf-8" ?>
<ContentPage xmlns="http://schemas.microsoft.com/dotnet/2021/maui"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
x:Class="Maui.Controls.Sample.Issues.PageSafeAreaOrientationVSM"
Title="Page SafeArea VSM Test">
<!-- Apply VSM to the Page itself to change SafeAreaEdges based on orientation -->
<VisualStateManager.VisualStateGroups>
<VisualStateGroupList>
<VisualStateGroup x:Name="OrientationStates">
<VisualState x:Name="Portrait">
<VisualState.StateTriggers>
<OrientationStateTrigger Orientation="Portrait" />
</VisualState.StateTriggers>
<VisualState.Setters>
<Setter Property="SafeAreaEdges" Value="Container" />
</VisualState.Setters>
</VisualState>
<VisualState x:Name="Landscape">
<VisualState.StateTriggers>
<OrientationStateTrigger Orientation="Landscape" />
</VisualState.StateTriggers>
<VisualState.Setters>
<Setter Property="SafeAreaEdges" Value="Container" />
</VisualState.Setters>
</VisualState>
</VisualStateGroup>
</VisualStateGroupList>
</VisualStateManager.VisualStateGroups>

<ScrollView BackgroundColor="LightGray">
<VerticalStackLayout Spacing="20" Padding="20">

<Label Text="Page SafeArea with Orientation VSM Test"
FontSize="24"
FontAttributes="Bold"
HorizontalOptions="Center"/>

<Label Text="This test validates that Page SafeAreaEdges responds to orientation changes via VisualStateManager."
FontSize="14"
HorizontalOptions="Center"/>

<Border BackgroundColor="LightBlue"
Stroke="DarkBlue"
StrokeThickness="2"
Padding="15">
<VerticalStackLayout Spacing="10">
<Label Text="Current State"
FontSize="18"
FontAttributes="Bold"/>

<Label x:Name="OrientationLabel"
Text="Orientation: Detecting..."
FontSize="16"
AutomationId="OrientationLabel"/>

<Label x:Name="SafeAreaEdgesLabel"
Text="SafeAreaEdges: Detecting..."
FontSize="16"
AutomationId="SafeAreaEdgesLabel"/>

<Label x:Name="PageDimensionsLabel"
Text="Page Dimensions: Loading..."
FontSize="16"
AutomationId="PageDimensionsLabel"/>
</VerticalStackLayout>
</Border>

<Border BackgroundColor="LightYellow"
Stroke="Orange"
StrokeThickness="2"
Padding="15">
<VerticalStackLayout Spacing="10">
<Label Text="Test Instructions"
FontSize="18"
FontAttributes="Bold"/>

<Label Text="• Rotate device from Portrait to Landscape"
FontSize="14"/>
<Label Text="• Observe SafeAreaEdges updates automatically"
FontSize="14"/>
<Label Text="• The page safe area should be Container in both orientations"
FontSize="14"/>
</VerticalStackLayout>
</Border>

<!-- Visual markers to show safe area effect -->
<Border BackgroundColor="White"
Stroke="Red"
StrokeThickness="3"
Padding="10">
<VerticalStackLayout Spacing="5">
<Label Text="Visual Markers"
FontSize="18"
FontAttributes="Bold"/>

<BoxView Color="Red"
HeightRequest="50"
WidthRequest="100"
HorizontalOptions="Start"
AutomationId="TopLeftMarker"/>

<BoxView Color="Green"
HeightRequest="50"
WidthRequest="100"
HorizontalOptions="End"
AutomationId="TopRightMarker"/>

<BoxView Color="Blue"
HeightRequest="50"
WidthRequest="100"
HorizontalOptions="Center"
AutomationId="CenterMarker"/>
</VerticalStackLayout>
</Border>

<Label Text="End of Content"
FontSize="18"
FontAttributes="Bold"
HorizontalOptions="Center"/>

</VerticalStackLayout>
</ScrollView>
</ContentPage>
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
namespace Maui.Controls.Sample.Issues;

[Issue(IssueTracker.None, 0, "Page SafeAreaEdges with OrientationStateTrigger VSM Test", PlatformAffected.All)]
public partial class PageSafeAreaOrientationVSM : ContentPage
{
public PageSafeAreaOrientationVSM()
{
InitializeComponent();

// Monitor size changes for orientation detection
this.SizeChanged += OnSizeChanged;

// Update info when page appears
this.Appearing += OnPageAppearing;
}

private void OnPageAppearing(object sender, EventArgs e)
{
// Delay to allow layout to complete
Dispatcher.DispatchDelayed(TimeSpan.FromMilliseconds(500), UpdateInfo);
}

private void OnSizeChanged(object sender, EventArgs e)
{
// Update info when size changes (indicating orientation change)
Dispatcher.DispatchDelayed(TimeSpan.FromMilliseconds(100), UpdateInfo);
}

private void UpdateInfo()
{
try
{
// Determine orientation based on actual page dimensions
var isLandscape = this.Width > this.Height;
var orientationText = isLandscape ? "Landscape" : "Portrait";

OrientationLabel.Text = $"Orientation: {orientationText}";
SafeAreaEdgesLabel.Text = $"SafeAreaEdges: {this.SafeAreaEdges}";
PageDimensionsLabel.Text = $"Page Dimensions: {this.Width:F1} x {this.Height:F1}";
}
catch (Exception ex)
{
OrientationLabel.Text = $"Orientation: Error - {ex.Message}";
SafeAreaEdgesLabel.Text = "SafeAreaEdges: Error";
PageDimensionsLabel.Text = "Page Dimensions: Error";
}
}
}
Loading