Skip to content

Commit 5977089

Browse files
more changes
1 parent 41c305f commit 5977089

File tree

6 files changed

+99
-55
lines changed

6 files changed

+99
-55
lines changed

src/Core/src/Handlers/ScrollView/ScrollViewHandler.Android.cs

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
using Android.Views;
2+
using AndroidX.Core.View;
23
using Microsoft.Maui.Graphics;
34
using static Microsoft.Maui.Layouts.LayoutExtensions;
45

@@ -225,6 +226,12 @@ static void InsertInsetView(IScrollViewHandler handler, IScrollView scrollView,
225226
Tag = InsetPanelTag
226227
};
227228

229+
// replace the one that's part of contentviewgroup for now
230+
ViewCompat.SetOnApplyWindowInsetsListener(paddingShim, null);
231+
232+
// Setting this here on the scrollview and that's where we apply insets
233+
ViewCompat.SetOnApplyWindowInsetsListener(handler.PlatformView, new WindowsListener());
234+
228235
handler.PlatformView.RemoveAllViews();
229236
paddingShim.AddView(nativeContent);
230237
handler.PlatformView.SetContent(paddingShim);
@@ -265,4 +272,17 @@ Size ICrossPlatformLayout.CrossPlatformMeasure(double widthConstraint, double he
265272
Size ICrossPlatformLayout.CrossPlatformArrange(Rect bounds) =>
266273
(VirtualView as ICrossPlatformLayout)?.CrossPlatformArrange(bounds) ?? Size.Zero;
267274
}
275+
276+
internal class WindowsListener : Java.Lang.Object, IOnApplyWindowInsetsListener
277+
{
278+
public WindowInsetsCompat? OnApplyWindowInsets(View? v, WindowInsetsCompat? insets)
279+
{
280+
// I'm not sure the best way to apply insets here. Need to test more
281+
// 1) if the user has set all safeareaedges to all or container do we just set fitToSystenWindow on scrollview?
282+
// 2) do we just apply padding to the scrollview and call that good?
283+
// 3) we probably need
284+
285+
return WindowInsetsCompat.Consumed;
286+
}
287+
}
268288
}

src/Core/src/Handlers/Window/WindowHandler.Android.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,7 @@ public static void MapContent(IWindowHandler handler, IWindow window)
2828
_ = handler.MauiContext ?? throw new InvalidOperationException($"{nameof(MauiContext)} should have been set by base class.");
2929

3030
var rootView = CreateRootViewFromContent(handler, window);
31-
ViewCompat.SetOnApplyWindowInsetsListener(rootView, new WindowsListener());
31+
//ViewCompat.SetOnApplyWindowInsetsListener(rootView, new WindowsListener());
3232
handler.PlatformView.SetContentView(rootView);
3333
}
3434

src/Core/src/Platform/Android/ContentViewGroup.cs

Lines changed: 25 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@
77
using AndroidX.Core.View;
88
using Microsoft.Maui.Graphics;
99
using Microsoft.Maui.Graphics.Platform;
10+
using Rectangle = Microsoft.Maui.Graphics.Rect;
1011

1112
namespace Microsoft.Maui.Platform
1213
{
@@ -93,15 +94,23 @@ protected override void OnMeasure(int widthMeasureSpec, int heightMeasureSpec)
9394
var deviceIndependentWidth = widthMeasureSpec.ToDouble(_context);
9495
var deviceIndependentHeight = heightMeasureSpec.ToDouble(_context);
9596

97+
// Account for padding in available space
98+
var paddingLeft = _context.FromPixels(PaddingLeft);
99+
var paddingTop = _context.FromPixels(PaddingTop);
100+
var paddingRight = _context.FromPixels(PaddingRight);
101+
var paddingBottom = _context.FromPixels(PaddingBottom);
102+
103+
var availableWidth = Math.Max(0, deviceIndependentWidth - paddingLeft - paddingRight);
104+
var availableHeight = Math.Max(0, deviceIndependentHeight - paddingTop - paddingBottom);
105+
96106
var widthMode = MeasureSpec.GetMode(widthMeasureSpec);
97107
var heightMode = MeasureSpec.GetMode(heightMeasureSpec);
98-
// TODO make measure sync with safearea
99-
var measure = CrossPlatformMeasure(deviceIndependentWidth, deviceIndependentHeight);
108+
var measure = CrossPlatformMeasure(availableWidth, availableHeight);
100109

101110
// If the measure spec was exact, we should return the explicit size value, even if the content
102111
// measure came out to a different size
103-
var width = widthMode == MeasureSpecMode.Exactly ? deviceIndependentWidth : measure.Width;
104-
var height = heightMode == MeasureSpecMode.Exactly ? deviceIndependentHeight : measure.Height;
112+
var width = widthMode == MeasureSpecMode.Exactly ? deviceIndependentWidth : measure.Width + paddingLeft + paddingRight;
113+
var height = heightMode == MeasureSpecMode.Exactly ? deviceIndependentHeight : measure.Height + paddingTop + paddingBottom;
105114

106115
var platformWidth = _context.ToPixels(width);
107116
var platformHeight = _context.ToPixels(height);
@@ -121,11 +130,18 @@ protected override void OnLayout(bool changed, int left, int top, int right, int
121130
}
122131

123132
var destination = _context.ToCrossPlatformRectInReferenceFrame(left, top, right, bottom);
124-
// Apply safe area adjustments if needed
125-
if (_safeAreaHandler.RespondsToSafeArea())
126-
{
127-
destination = _safeAreaHandler.AdjustForSafeArea(destination);
128-
}
133+
134+
// Account for padding in layout bounds
135+
var paddingLeft = _context.FromPixels(PaddingLeft);
136+
var paddingTop = _context.FromPixels(PaddingTop);
137+
var paddingRight = _context.FromPixels(PaddingRight);
138+
var paddingBottom = _context.FromPixels(PaddingBottom);
139+
140+
destination = new Rectangle(
141+
destination.X + paddingLeft,
142+
destination.Y + paddingTop,
143+
Math.Max(0, destination.Width - paddingLeft - paddingRight),
144+
Math.Max(0, destination.Height - paddingTop - paddingBottom));
129145

130146
CrossPlatformArrange(destination);
131147
}

src/Core/src/Platform/Android/LayoutViewGroup.cs

Lines changed: 23 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -102,15 +102,23 @@ protected override void OnMeasure(int widthMeasureSpec, int heightMeasureSpec)
102102
var deviceIndependentWidth = widthMeasureSpec.ToDouble(_context);
103103
var deviceIndependentHeight = heightMeasureSpec.ToDouble(_context);
104104

105+
// Account for padding in available space
106+
var paddingLeft = _context.FromPixels(PaddingLeft);
107+
var paddingTop = _context.FromPixels(PaddingTop);
108+
var paddingRight = _context.FromPixels(PaddingRight);
109+
var paddingBottom = _context.FromPixels(PaddingBottom);
110+
111+
var availableWidth = Math.Max(0, deviceIndependentWidth - paddingLeft - paddingRight);
112+
var availableHeight = Math.Max(0, deviceIndependentHeight - paddingTop - paddingBottom);
113+
105114
var widthMode = MeasureSpec.GetMode(widthMeasureSpec);
106115
var heightMode = MeasureSpec.GetMode(heightMeasureSpec);
107-
// TODO make measure sync with safearea
108-
var measure = CrossPlatformMeasure(deviceIndependentWidth, deviceIndependentHeight);
116+
var measure = CrossPlatformMeasure(availableWidth, availableHeight);
109117

110118
// If the measure spec was exact, we should return the explicit size value, even if the content
111119
// measure came out to a different size
112-
var width = widthMode == MeasureSpecMode.Exactly ? deviceIndependentWidth : measure.Width;
113-
var height = heightMode == MeasureSpecMode.Exactly ? deviceIndependentHeight : measure.Height;
120+
var width = widthMode == MeasureSpecMode.Exactly ? deviceIndependentWidth : measure.Width + paddingLeft + paddingRight;
121+
var height = heightMode == MeasureSpecMode.Exactly ? deviceIndependentHeight : measure.Height + paddingTop + paddingBottom;
114122

115123
var platformWidth = _context.ToPixels(width);
116124
var platformHeight = _context.ToPixels(height);
@@ -134,11 +142,17 @@ protected override void OnLayout(bool changed, int l, int t, int r, int b)
134142

135143
var destination = _context.ToCrossPlatformRectInReferenceFrame(l, t, r, b);
136144

137-
// Apply safe area adjustments if needed
138-
if (_safeAreaHandler.RespondsToSafeArea())
139-
{
140-
destination = _safeAreaHandler.AdjustForSafeArea(destination);
141-
}
145+
// Account for padding in layout bounds
146+
var paddingLeft = _context.FromPixels(PaddingLeft);
147+
var paddingTop = _context.FromPixels(PaddingTop);
148+
var paddingRight = _context.FromPixels(PaddingRight);
149+
var paddingBottom = _context.FromPixels(PaddingBottom);
150+
151+
destination = new Rectangle(
152+
destination.X + paddingLeft,
153+
destination.Y + paddingTop,
154+
Math.Max(0, destination.Width - paddingLeft - paddingRight),
155+
Math.Max(0, destination.Height - paddingTop - paddingBottom));
142156

143157
CrossPlatformArrange(destination);
144158

src/Core/src/Platform/Android/SafeAreaHandler.cs

Lines changed: 29 additions & 35 deletions
Original file line numberDiff line numberDiff line change
@@ -26,20 +26,20 @@ internal class SafeAreaHandler(View owner, Context context, Func<ICrossPlatformL
2626
bool? _scrollViewDescendant;
2727

2828
internal IOnApplyWindowInsetsListener GetWindowInsetsListener() =>
29-
new WindowInsetsListener(this, () => _owner.RequestLayout());
29+
new WindowInsetsListener(this);
3030

3131
/// <summary>
3232
/// Forces a re-application of window insets when safe area configuration changes.
3333
/// This ensures OnApplyWindowInsets is called before measure and arrange.
3434
/// </summary>
3535
internal void InvalidateWindowInsets()
3636
{
37-
if (_lastReceivedInsets is not null)
37+
/*if (_lastReceivedInsets is not null)
3838
{
3939
// Re-apply the last received insets to trigger OnApplyWindowInsets
4040
ViewCompat.DispatchApplyWindowInsets(_owner, _lastReceivedInsets);
4141
}
42-
else
42+
else*/
4343
{
4444
// Request fresh insets from the system
4545
ViewCompat.RequestApplyInsets(_owner);
@@ -164,10 +164,10 @@ bool ValidateSafeArea()
164164
internal void UpdateSafeAreaConfiguration()
165165
{
166166
// Clear cached ScrollView descendant check
167-
_scrollViewDescendant = null;
167+
// _scrollViewDescendant = null;
168168

169169
// Force re-calculation of safe area
170-
var oldSafeArea = _safeArea;
170+
// var oldSafeArea = _safeArea;
171171
_safeArea = GetAdjustedSafeAreaInsets();
172172

173173
// Always invalidate insets when configuration changes, regardless of whether
@@ -194,10 +194,9 @@ internal static bool HasAnyRegions(ISafeAreaView2 sav2)
194194
/// <summary>
195195
/// WindowInsets listener for ContentViewGroup and LayoutViewGroup.
196196
/// </summary>
197-
public class WindowInsetsListener(SafeAreaHandler handler, Action requestLayout) : Java.Lang.Object, IOnApplyWindowInsetsListener
197+
public class WindowInsetsListener(SafeAreaHandler handler) : Java.Lang.Object, IOnApplyWindowInsetsListener
198198
{
199199
readonly WeakReference<SafeAreaHandler> _handlerRef = new(handler ?? throw new ArgumentNullException(nameof(handler)));
200-
readonly WeakReference<Action> _requestLayoutRef = new(requestLayout ?? throw new ArgumentNullException(nameof(requestLayout)));
201200

202201
public WindowInsetsCompat OnApplyWindowInsets(View v, WindowInsetsCompat insets)
203202
{
@@ -208,7 +207,7 @@ public WindowInsetsCompat OnApplyWindowInsets(View v, WindowInsetsCompat insets)
208207
}
209208

210209
// Get handler and requestLayout from weak references
211-
if (!_handlerRef.TryGetTarget(out var handler) || !_requestLayoutRef.TryGetTarget(out var requestLayout))
210+
if (!_handlerRef.TryGetTarget(out var handler))
212211
{
213212
// Handler or requestLayout has been garbage collected, return insets unchanged
214213
return insets;
@@ -223,40 +222,35 @@ public WindowInsetsCompat OnApplyWindowInsets(View v, WindowInsetsCompat insets)
223222

224223
handler.UpdateKeyboardState(keyboardInsets, isKeyboardShowing);
225224

226-
// Request layout if keyboard state changed
227-
if (wasKeyboardShowing != isKeyboardShowing)
228-
{
229-
requestLayout();
230-
}
225+
var viewInsets = handler.GetAdjustedSafeAreaInsets();
226+
var context = handler._context;
231227

232-
requestLayout();
228+
v.SetPadding((int)context.ToPixels(viewInsets.Left), (int)context.ToPixels(viewInsets.Top), (int)context.ToPixels(viewInsets.Right), (int)context.ToPixels(viewInsets.Bottom));
233229

234-
var crossPlatformLayout = handler._getCrossPlatformLayout();
235230

236-
// Use RespondsToSafeArea to check if this view should handle safe area
237-
if (!handler.RespondsToSafeArea())
231+
// This is just a quick hack to demonstrate the idea
232+
// in the real implementation you would check each of these insets against the relative SafeAreaRegions
233+
// and just specify which range is consumed or not
234+
if (viewInsets.Top > 0 || viewInsets.Bottom > 0 || viewInsets.Left > 0 || viewInsets.Right > 0)
238235
{
239-
// For deeper descendants of ScrollView, consume all insets to prevent double-application
240-
return ConsumeAllInsets(insets);
241-
}
242236

243-
if (handler._getCrossPlatformLayout() is ISafeAreaView2 sav2 && HasAnyRegions(sav2))
244-
{
245-
return ConsumeAllInsets(insets);
237+
// This is also somewhat of a hack, AFAICT you need to reset the padding on all children that were previouslly consuming the insets
238+
// There's probably a more efficient way to do this
239+
// One approach here could be for us to just make one global SafeAreaHandler that gets applied to every view
240+
// and then that handler would have a tracking list of views so it could know where to dispatch and reset.
241+
var descendant = v.FindDescendantView<ViewGroup>((view) => view is ICrossPlatformLayoutBacking && view != v);
242+
descendant?.DispatchApplyWindowInsets(WindowInsets.Consumed);
243+
244+
while (descendant != null)
245+
{
246+
descendant = descendant.FindDescendantView<ViewGroup>((view) => view is ICrossPlatformLayoutBacking && view != descendant);
247+
descendant?.DispatchApplyWindowInsets(WindowInsets.Consumed);
248+
}
249+
250+
return WindowInsetsCompat.Consumed;
246251
}
247252

248-
// This view is not a ScrollView descendant, pass insets through unchanged
249253
return insets;
250254
}
251-
252-
static WindowInsetsCompat ConsumeAllInsets(WindowInsetsCompat insets)
253-
{
254-
// Consume all insets to prevent safe area handling for ScrollView descendants
255-
return new WindowInsetsCompat.Builder(insets)
256-
.SetInsets(WindowInsetsCompat.Type.SystemBars(), AndroidX.Core.Graphics.Insets.None)
257-
.SetInsets(WindowInsetsCompat.Type.DisplayCutout(), AndroidX.Core.Graphics.Insets.None)
258-
.SetInsets(WindowInsetsCompat.Type.Ime(), AndroidX.Core.Graphics.Insets.None)
259-
.Build();
260-
}
261255
}
262-
}
256+
}

src/Core/src/Platform/Android/ViewGroupExtensions.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -57,7 +57,7 @@ public static bool TryGetFirstChildOfType<T>(this AViewGroup viewGroup, [NotNull
5757

5858
internal static T? GetChildAt<T>(this AView view, int index) where T : AView
5959
{
60-
if (view is AViewGroup viewGroup && viewGroup.ChildCount < index)
60+
if (view is AViewGroup viewGroup && index < viewGroup.ChildCount)
6161
return (T?)viewGroup.GetChildAt(index);
6262

6363
return null;

0 commit comments

Comments
 (0)