Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
30 commits
Select commit Hold shift + click to select a range
8c06b0f
Expose safe area contracts for custom views
Aug 22, 2026
ecf8393
Fix safe area review feedback
Aug 23, 2026
dc737cb
Fix nested safe area updates on Apple platforms
Aug 23, 2026
5eb242e
Refine safe area mapping and validation
Aug 23, 2026
2185a28
Refine public safe-area contract
Aug 24, 2026
0cf1a57
Limit safe-area changes to product scope
Aug 24, 2026
92bad6c
Strengthen nested safe area invalidation coverage
Aug 24, 2026
b2e5216
Document custom safe area defaults
Aug 25, 2026
064d85c
Preserve legacy scroll safe area behavior
Aug 25, 2026
48fe0ad
Optimize safe area edge resolution
Aug 25, 2026
5fef2f8
Test mixed custom safe area defaults
Aug 25, 2026
84c2a6c
Handle overlapping safe area edges independently
Aug 25, 2026
7ea5fcd
Honor safe area edges in inherited containers
Aug 25, 2026
7b5118d
Harden safe area state transitions
Aug 25, 2026
f6db02a
Address final safe area review findings
Aug 25, 2026
f5a35e4
Expose effective safe area strategy
Aug 25, 2026
612020e
Fix safe area AOT profile type records
Aug 25, 2026
e5dde59
Prune unreachable safe area AOT entry
Aug 25, 2026
e6496d7
Preserve nested keyboard safe area padding
Aug 25, 2026
777f65e
Clarify safe area precedence guidance
Aug 25, 2026
490a33f
Keep safe area fix product-scoped
Aug 26, 2026
c32e41e
Handle nested soft input residuals
Aug 26, 2026
310d7e9
Cover transformed keyboard safe area geometry
Aug 26, 2026
c265a30
Address final safe area review feedback
Aug 26, 2026
542e0e9
Merge net11.0 into safe area API branch
Aug 26, 2026
104f814
Compare keyboard overlap at pixel boundaries
Aug 26, 2026
79cdc74
Harden iOS safe area edge handling
Aug 26, 2026
3d3c42b
Address safe area review follow-ups
Aug 27, 2026
ac8d98c
Address final safe area review feedback
Aug 27, 2026
92f72f9
Document safe area correctness invariants
Aug 27, 2026
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
8 changes: 4 additions & 4 deletions .github/agents/maui-expert-reviewer.md
Original file line number Diff line number Diff line change
Expand Up @@ -279,14 +279,14 @@ Safe area adjustments, keyboard insets, and ancestor hierarchy walks. See `safe-
- CHECK: Safe area comparison uses pixel-level tolerance to absorb sub-pixel animation noise
- CHECK: Never gate per-view safe area callbacks on window-level insets (they diverge on MacCatalyst with custom TitleBar)
- CHECK: Safe area caches are invalidated on inset changes and window transitions
- CHECK: Only `ISafeAreaView`/`ISafeAreaView2` views receive safe area adjustments — non-safe-area views must return empty padding
- CHECK: Only legacy `ISafeAreaView` or modern `ISafeAreaElement` views receive safe area adjustments — non-safe-area views must return empty padding
- CHECK: Raw vs adjusted inset comparison — `_safeArea` is filtered by `GetSafeAreaForEdge`; raw `UIView.SafeAreaInsets` includes all edges; never compare across types
- CHECK: Use constants for magic strings — property names like `"SafeAreaInsets"` must be constants, not bare strings
- CHECK: New safe area types belong in `src/Core` so `ISafeAreaView2` can reference them — don't add core interface deps to `Controls.csproj`
- CHECK: Before creating versioned interfaces (`ISafeAreaView2`), check if the existing interface can be extended with default interface methods
- CHECK: New safe area types belong in `src/Core` so `ISafeAreaElement` can reference them — don't add core interface deps to `Controls.csproj`
- CHECK: Prefer a capability-oriented contract such as `ISafeAreaElement` over publishing a numbered interface

#### Platform notes
- **iOS/MacCatalyst**: See `safe-area-ios.instructions.md` for `IsParentHandlingSafeArea`, `EqualsAtPixelLevel`, and the Window Guard anti-pattern. macCatalyst defaults `UseSafeArea` to `true` (unlike iOS where it's `false`).
- **iOS/MacCatalyst**: See `safe-area-ios.instructions.md` for `GetParentHandledSafeAreaEdges`, `EqualsAtPixelLevel`, and the Window Guard anti-pattern. macCatalyst defaults `UseSafeArea` to `true` (unlike iOS where it's `false`).
- **Android**: `WindowInsetsCompat` for keyboard and system bar; `fitsSystemWindows` behavior differs by API level

### 16. Complexity Reduction `[minor]`
Expand Down
6 changes: 4 additions & 2 deletions .github/instructions/safe-area-ios.instructions.md
Original file line number Diff line number Diff line change
Expand Up @@ -23,9 +23,11 @@ Local macOS 26+ testing does NOT validate CI behavior. Fixes must pass CI on mac

## Architecture (PR #34024)

**`IsParentHandlingSafeArea`** — before applying adjustments, `MauiView`/`MauiScrollView` walk ancestors to check if any ancestor handles the **same edges**. If so, descendant skips (avoids double-padding). Edge-aware: parent handling `Top` does not block child handling `Bottom`. Result cached in `bool? _parentHandlesSafeArea`; cleared on `SafeAreaInsetsDidChange`, `InvalidateSafeArea`, `MovedToWindow`. `AppliesSafeAreaAdjustments` is `internal` for cross-type ancestor checks.
**`GetParentHandledSafeAreaEdges`** — before applying adjustments, `MauiView`/`MauiScrollView` walk ancestors and resolve each ancestor's current safe-area inputs. Descendants suppress only overlapping edges, so a parent handling `Top` does not block a child handling `Bottom`. Insets are classified as zero or nonzero at device-pixel resolution, and traversal stops as soon as all four edges are handled. Results are cached in `SafeAreaEdges? _parentHandledSafeAreaEdges`; `SafeAreaInsetsDidChange`, `InvalidateSafeArea`, and `MovedToWindow` clear the cache. Property and keyboard notifications call `MauiView.InvalidateSafeArea(UIView)` to invalidate the full native subtree. A later pixel-level change in an ancestor's applied safe area also invalidates its descendants, which keeps non-`SoftInput` descendants synchronized during multi-pass keyboard layout.

**`EqualsAtPixelLevel`** — safe area compared at device-pixel resolution to absorb sub-pixel animation noise (`0.0000001pt` during `TranslateToAsync`), preventing oscillation loops (#32586, #33934).
**Nested keyboard overlap** — `UIKeyboard.FrameEndUserInfoKey` is converted from screen coordinates into the current window before a `Bottom = SoftInput` inset is calculated from each `MauiView`'s transformed frame in that window. A positive child overlap is preserved even when an ancestor also handles `Bottom`, because the ancestor's inset may not cover a non-uniform or not-yet-rearranged child frame. Once the parent arranges the child above the keyboard, the child's overlap becomes zero and ordinary ancestor suppression prevents double-padding. While the keyboard is visible, bottom-`SoftInput` views compare this frame-dependent overlap on every layout but clear the cached ancestor edges only when it changes at device-pixel resolution.

**Pixel-level comparisons** — use `IsZeroAtPixelLevel` when classifying ancestor insets and `EqualsAtPixelLevel` when comparing cached safe areas. Both operate at device-pixel resolution to absorb sub-pixel animation noise (`0.0000001pt` during `TranslateToAsync`), preventing false edge suppression and oscillation loops (#32586, #33934).

## Anti-Patterns

Expand Down
Binary file not shown.
Original file line number Diff line number Diff line change
Expand Up @@ -347,6 +347,7 @@ Methods:
bool Microsoft.Maui.RuntimeFeature:get_EnableMauiDiagnostics ()
bool Microsoft.Maui.RuntimeFeature:get_IsMaterial3Enabled ()
bool Microsoft.Maui.RuntimeFeature:get_UseMauiAndroidSystemBarBackgrounds ()
bool Microsoft.Maui.SafeAreaViewStrategy:TryGetSafeAreaEdges (object,Microsoft.Maui.SafeAreaEdges&,bool)
bool Microsoft.Maui.Storage.FileSystemUtils:IsValidRelativePath (string)
bool Microsoft.Maui.ViewExtensions:NeedsContainer (Microsoft.Maui.IView)
bool Microsoft.Maui.VisualDiagnostics:get_IsEnabled ()
Expand Down Expand Up @@ -2259,7 +2260,6 @@ Methods:
Microsoft.Maui.IMauiHandlersFactory Microsoft.Maui.MauiContext:<.ctor>b__7_0 ()
Microsoft.Maui.IMauiHandlersFactory Microsoft.Maui.MauiContext:get_Handlers ()
Microsoft.Maui.IPersistedState Microsoft.Maui.ActivationState:get_State ()
Microsoft.Maui.ISafeAreaView2 Microsoft.Maui.Platform.SafeAreaExtensions:GetSafeAreaView2 (object)
Microsoft.Maui.IShadow Microsoft.Maui.Controls.VisualElement:Microsoft.Maui.IView.get_Shadow ()
Microsoft.Maui.IToolbar Microsoft.Maui.Controls.Page:Microsoft.Maui.IToolbarElement.get_Toolbar ()
Microsoft.Maui.IToolbar Microsoft.Maui.Controls.Window:Microsoft.Maui.IToolbarElement.get_Toolbar ()
Expand Down Expand Up @@ -2319,8 +2319,7 @@ Methods:
Microsoft.Maui.Primitives.LayoutAlignment Microsoft.Maui.Controls.View:Microsoft.Maui.IView.get_VerticalLayoutAlignment ()
Microsoft.Maui.PropertyMapper Microsoft.Maui.Controls.View:Microsoft.Maui.IPropertyMapperView.GetPropertyMapperOverrides ()
Microsoft.Maui.SafeAreaEdges Microsoft.Maui.SafeAreaEdges:get_Default ()
Microsoft.Maui.SafeAreaRegions Microsoft.Maui.Controls.ContentPage:Microsoft.Maui.ISafeAreaView2.GetSafeAreaRegionsForEdge (int)
Microsoft.Maui.SafeAreaRegions Microsoft.Maui.Platform.SafeAreaExtensions:GetSafeAreaRegionForEdge (int,Microsoft.Maui.ICrossPlatformLayout)
Microsoft.Maui.SafeAreaRegions Microsoft.Maui.Controls.ContentPage:Microsoft.Maui.ISafeAreaViewStrategy.GetSafeAreaRegionsForEdge (int)
Microsoft.Maui.Semantics Microsoft.Maui.Controls.SemanticProperties:UpdateSemantics (Microsoft.Maui.Controls.BindableObject,Microsoft.Maui.Semantics)
Microsoft.Maui.Semantics Microsoft.Maui.Controls.VisualElement:Microsoft.Maui.IView.get_Semantics ()
Microsoft.Maui.Semantics Microsoft.Maui.Controls.VisualElement:UpdateSemantics ()
Expand Down Expand Up @@ -5485,7 +5484,7 @@ Methods:
void Microsoft.Maui.Controls.BindableObject:.ctor ()
void Microsoft.Maui.Controls.BindableObject:add_PropertyChanged (System.ComponentModel.PropertyChangedEventHandler)
void Microsoft.Maui.Controls.BindableObject:add_PropertyChanging (Microsoft.Maui.Controls.PropertyChangingEventHandler)
void Microsoft.Maui.Controls.BindableObject:OnBindablePropertySet (Microsoft.Maui.Controls.BindableProperty,object,object,bool,bool)
void Microsoft.Maui.Controls.BindableObject:OnBindablePropertySet (Microsoft.Maui.Controls.BindableProperty,object,object,bool,bool,bool)
void Microsoft.Maui.Controls.BindableObject:OnPropertyChanged (string)
void Microsoft.Maui.Controls.BindableObject:OnPropertyChanging (string)
void Microsoft.Maui.Controls.BindableObject:SetDynamicResource (Microsoft.Maui.Controls.BindableProperty,string,Microsoft.Maui.Controls.SetterSpecificity)
Expand Down Expand Up @@ -5557,7 +5556,7 @@ Methods:
void Microsoft.Maui.Controls.Element:MapAutomationPropertiesIsInAccessibleTree (Microsoft.Maui.IElementHandler,Microsoft.Maui.Controls.Element)
void Microsoft.Maui.Controls.Element:MapAutomationPropertiesIsInAccessibleTree (Microsoft.Maui.IElementHandler,Microsoft.Maui.IElement)
void Microsoft.Maui.Controls.Element:Microsoft.Maui.Controls.IElementDefinition.AddResourcesChangedListener (System.Action`2<object, Microsoft.Maui.Controls.Internals.ResourcesChangedEventArgs>)
void Microsoft.Maui.Controls.Element:OnBindablePropertySet (Microsoft.Maui.Controls.BindableProperty,object,object,bool,bool)
void Microsoft.Maui.Controls.Element:OnBindablePropertySet (Microsoft.Maui.Controls.BindableProperty,object,object,bool,bool,bool)
void Microsoft.Maui.Controls.Element:OnChildAdded (Microsoft.Maui.Controls.Element)
void Microsoft.Maui.Controls.Element:OnDescendantAdded (Microsoft.Maui.Controls.Element)
void Microsoft.Maui.Controls.Element:OnDescendantAddedCore (Microsoft.Maui.Controls.Element,Microsoft.Maui.Controls.ElementEventArgs)
Expand Down
Binary file not shown.
Original file line number Diff line number Diff line change
Expand Up @@ -390,7 +390,7 @@ Methods:
bool Microsoft.Maui.Controls.BindableObjectExtensions:GetPropertyIfSet (Microsoft.Maui.Controls.BindableObject,Microsoft.Maui.Controls.BindableProperty,bool)
bool Microsoft.Maui.Controls.BindableProperty:TryConvert (object&)
bool Microsoft.Maui.Controls.BindingExpressionHelper:TryConvert (object&,Microsoft.Maui.Controls.BindableProperty,System.Type,bool)
bool Microsoft.Maui.Controls.Border:Microsoft.Maui.ISafeAreaView2.get_HasExplicitSafeAreaEdges ()
bool Microsoft.Maui.Controls.Border:Microsoft.Maui.ISafeAreaElement.get_HasExplicitSafeAreaEdges ()
bool Microsoft.Maui.Controls.Brush:IsNullOrEmpty (Microsoft.Maui.Controls.Brush)
bool Microsoft.Maui.Controls.Button:get_FontAutoScalingEnabled ()
bool Microsoft.Maui.Controls.Button:get_IsEnabledCore ()
Expand Down Expand Up @@ -453,7 +453,7 @@ Methods:
bool Microsoft.Maui.Controls.Layout:get_CascadeInputTransparent ()
bool Microsoft.Maui.Controls.Layout:get_IsClippedToBounds ()
bool Microsoft.Maui.Controls.Layout:Microsoft.Maui.ILayout.get_ClipsToBounds ()
bool Microsoft.Maui.Controls.Layout:Microsoft.Maui.ISafeAreaView2.get_HasExplicitSafeAreaEdges ()
bool Microsoft.Maui.Controls.Layout:Microsoft.Maui.ISafeAreaElement.get_HasExplicitSafeAreaEdges ()
bool Microsoft.Maui.Controls.LayoutOptions:Equals (object)
bool Microsoft.Maui.Controls.LinearItemsLayout/<>c:<.cctor>b__13_0 (Microsoft.Maui.Controls.BindableObject,object)
bool Microsoft.Maui.Controls.MenuItemTracker`1/<get_AdditionalTargets>d__7<Microsoft.Maui.Controls.MenuBarItem>:MoveNext ()
Expand Down Expand Up @@ -659,6 +659,7 @@ Methods:
bool Microsoft.Maui.RuntimeFeature:get_UseMauiAndroidSystemBarBackgrounds ()
bool Microsoft.Maui.SafeAreaEdges:Equals (Microsoft.Maui.SafeAreaEdges)
bool Microsoft.Maui.SafeAreaEdges:Equals (object)
bool Microsoft.Maui.SafeAreaViewStrategy:TryGetSafeAreaEdges (object,Microsoft.Maui.SafeAreaEdges&,bool)
bool Microsoft.Maui.Storage.PreferencesImplementation:ContainsKey (string,string)
bool Microsoft.Maui.StringExtensions:ContainsChar (string,char)
bool Microsoft.Maui.Thickness:Equals (Microsoft.Maui.Thickness)
Expand Down Expand Up @@ -3170,7 +3171,6 @@ Methods:
Microsoft.Maui.IPlatformViewHandler Microsoft.Maui.Controls.Handlers.Items.ItemContentView:CreateHandler (Microsoft.Maui.Controls.View,Microsoft.Maui.Controls.ItemsView)
Microsoft.Maui.IPlatformViewHandler Microsoft.Maui.Controls.Handlers.Items.TemplateHelpers:GetHandler (Microsoft.Maui.Controls.View,Microsoft.Maui.IMauiContext)
Microsoft.Maui.IPlatformViewHandler Microsoft.Maui.Platform.ViewExtensions:ToHandler (Microsoft.Maui.IView,Microsoft.Maui.IMauiContext)
Microsoft.Maui.ISafeAreaView2 Microsoft.Maui.Platform.SafeAreaExtensions:GetSafeAreaView2 (object)
Microsoft.Maui.IScrollView Microsoft.Maui.Handlers.ScrollViewHandler:Microsoft.Maui.Handlers.IScrollViewHandler.get_VirtualView ()
Microsoft.Maui.IShadow Microsoft.Maui.Controls.VisualElement:Microsoft.Maui.IView.get_Shadow ()
Microsoft.Maui.ITab Microsoft.Maui.Controls.Handlers.ShellItemTabbedViewAdapter:get_CurrentTab ()
Expand Down Expand Up @@ -3284,14 +3284,13 @@ Methods:
Microsoft.Maui.Primitives.LayoutAlignment Microsoft.Maui.Controls.View:Microsoft.Maui.IView.get_VerticalLayoutAlignment ()
Microsoft.Maui.PropertyMapper Microsoft.Maui.Controls.View:Microsoft.Maui.IPropertyMapperView.GetPropertyMapperOverrides ()
Microsoft.Maui.SafeAreaEdges Microsoft.Maui.Controls.Layout:get_SafeAreaEdges ()
Microsoft.Maui.SafeAreaEdges Microsoft.Maui.Controls.Layout:Microsoft.Maui.ISafeAreaElement.SafeAreaEdgesDefaultValueCreator ()
Microsoft.Maui.SafeAreaEdges Microsoft.Maui.Controls.Layout:Microsoft.Maui.ISafeAreaElement.GetDefaultSafeAreaEdges ()
Microsoft.Maui.SafeAreaEdges Microsoft.Maui.Controls.ScrollView:get_SafeAreaEdges ()
Microsoft.Maui.SafeAreaEdges Microsoft.Maui.Controls.ScrollView:Microsoft.Maui.ISafeAreaElement.SafeAreaEdgesDefaultValueCreator ()
Microsoft.Maui.SafeAreaEdges Microsoft.Maui.Controls.ScrollView:Microsoft.Maui.ISafeAreaElement.GetDefaultSafeAreaEdges ()
Microsoft.Maui.SafeAreaEdges Microsoft.Maui.SafeAreaEdges:get_Default ()
Microsoft.Maui.SafeAreaRegions Microsoft.Maui.Controls.ContentPage:Microsoft.Maui.ISafeAreaView2.GetSafeAreaRegionsForEdge (int)
Microsoft.Maui.SafeAreaRegions Microsoft.Maui.Controls.Layout:Microsoft.Maui.ISafeAreaView2.GetSafeAreaRegionsForEdge (int)
Microsoft.Maui.SafeAreaRegions Microsoft.Maui.Controls.ScrollView:Microsoft.Maui.ISafeAreaView2.GetSafeAreaRegionsForEdge (int)
Microsoft.Maui.SafeAreaRegions Microsoft.Maui.Platform.SafeAreaExtensions:GetSafeAreaRegionForEdge (int,Microsoft.Maui.ICrossPlatformLayout)
Microsoft.Maui.SafeAreaRegions Microsoft.Maui.Controls.ContentPage:Microsoft.Maui.ISafeAreaViewStrategy.GetSafeAreaRegionsForEdge (int)
Microsoft.Maui.SafeAreaRegions Microsoft.Maui.Controls.Layout:Microsoft.Maui.ISafeAreaViewStrategy.GetSafeAreaRegionsForEdge (int)
Microsoft.Maui.SafeAreaRegions Microsoft.Maui.Controls.ScrollView:Microsoft.Maui.ISafeAreaViewStrategy.GetSafeAreaRegionsForEdge (int)
Microsoft.Maui.SafeAreaRegions Microsoft.Maui.SafeAreaEdges:GetEdge (int)
Microsoft.Maui.ScrollBarVisibility Microsoft.Maui.Controls.ItemsView:get_HorizontalScrollBarVisibility ()
Microsoft.Maui.ScrollBarVisibility Microsoft.Maui.Controls.ItemsView:get_VerticalScrollBarVisibility ()
Expand Down Expand Up @@ -6442,7 +6441,7 @@ Methods:
void Microsoft.Maui.Controls.BindableObject:BindingContextPropertyChanged (Microsoft.Maui.Controls.BindableObject,object,object)
void Microsoft.Maui.Controls.BindableObject:ClearValue (Microsoft.Maui.Controls.BindableProperty,Microsoft.Maui.Controls.SetterSpecificity)
void Microsoft.Maui.Controls.BindableObject:ClearValueCore (Microsoft.Maui.Controls.BindableProperty,Microsoft.Maui.Controls.SetterSpecificity)
void Microsoft.Maui.Controls.BindableObject:OnBindablePropertySet (Microsoft.Maui.Controls.BindableProperty,object,object,bool,bool)
void Microsoft.Maui.Controls.BindableObject:OnBindablePropertySet (Microsoft.Maui.Controls.BindableProperty,object,object,bool,bool,bool)
void Microsoft.Maui.Controls.BindableObject:OnBindingContextChanged ()
void Microsoft.Maui.Controls.BindableObject:OnPropertyChanged (string)
void Microsoft.Maui.Controls.BindableObject:OnPropertyChanging (string)
Expand Down Expand Up @@ -6613,7 +6612,7 @@ Methods:
void Microsoft.Maui.Controls.Element:MapAutomationPropertiesIsInAccessibleTree (Microsoft.Maui.IElementHandler,Microsoft.Maui.IElement)
void Microsoft.Maui.Controls.Element:Microsoft.Maui.Controls.IElementDefinition.AddResourcesChangedListener (System.Action`2<object, Microsoft.Maui.Controls.Internals.ResourcesChangedEventArgs>)
void Microsoft.Maui.Controls.Element:Microsoft.Maui.Controls.IElementDefinition.RemoveResourcesChangedListener (System.Action`2<object, Microsoft.Maui.Controls.Internals.ResourcesChangedEventArgs>)
void Microsoft.Maui.Controls.Element:OnBindablePropertySet (Microsoft.Maui.Controls.BindableProperty,object,object,bool,bool)
void Microsoft.Maui.Controls.Element:OnBindablePropertySet (Microsoft.Maui.Controls.BindableProperty,object,object,bool,bool,bool)
void Microsoft.Maui.Controls.Element:OnBindingContextChanged ()
void Microsoft.Maui.Controls.Element:OnChildAdded (Microsoft.Maui.Controls.Element)
void Microsoft.Maui.Controls.Element:OnChildRemoved (Microsoft.Maui.Controls.Element,int)
Expand Down
Binary file not shown.
Loading
Loading