-
Notifications
You must be signed in to change notification settings - Fork 1.8k
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Remove gesture from page that interferes with accessibility (#10948)
* Apply gesture for closing keyboard as needed * - add entry for memory tests * - add tests * Update PublicAPI.Unshipped.txt * Update PublicAPI.Unshipped.txt * Update ResignFirstResponderTouchGestureRecognizer.cs * Update AssertionExtensions.Android.cs * Update AssertionExtensions.Android.cs * Update ResignFirstResponderTouchGestureRecognizer.cs * Update AssertionExtensions.Android.cs
- Loading branch information
Showing
15 changed files
with
277 additions
and
43 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
70 changes: 70 additions & 0 deletions
70
src/Controls/tests/DeviceTests/Elements/Window/WindowTests.iOS.cs
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,70 @@ | ||
using System.Linq; | ||
using System.Threading.Tasks; | ||
using Microsoft.Maui.Controls; | ||
using Microsoft.Maui.Platform; | ||
using Xunit; | ||
using Microsoft.Maui.Handlers; | ||
using System; | ||
|
||
namespace Microsoft.Maui.DeviceTests | ||
{ | ||
public partial class WindowTests | ||
{ | ||
[Theory] | ||
[InlineData(typeof(Editor))] | ||
[InlineData(typeof(Entry))] | ||
[InlineData(typeof(SearchBar))] | ||
public async Task FocusedTextInputAddsResignFirstResponderGesture(Type controlType) | ||
{ | ||
SetupBuilder(); | ||
var layout = new VerticalStackLayout(); | ||
var view = (View)Activator.CreateInstance(controlType); | ||
layout.Children.Add(view); | ||
|
||
await CreateHandlerAndAddToWindow<LayoutHandler>(layout, async (handler) => | ||
{ | ||
await OnLoadedAsync(view); | ||
view.Focus(); | ||
await AssertionExtensions.WaitForFocused(view); | ||
Assert.Contains(typeof(ResignFirstResponderTouchGestureRecognizer), | ||
handler.PlatformView.Window.GestureRecognizers.Select(x => x.GetType())); | ||
|
||
// Work around bug where iOS elements aren't toggling the "IsFocused" property | ||
(view as IView).IsFocused = true; | ||
view.Unfocus(); | ||
await AssertionExtensions.WaitForUnFocused(view); | ||
|
||
Assert.DoesNotContain(typeof(ResignFirstResponderTouchGestureRecognizer), | ||
handler.PlatformView.Window.GestureRecognizers.Select(x => x.GetType())); | ||
}); | ||
} | ||
|
||
[Theory] | ||
[InlineData(typeof(Editor))] | ||
[InlineData(typeof(Entry))] | ||
[InlineData(typeof(SearchBar))] | ||
public async Task RemovingControlFromWindowRemovesGesture(Type controlType) | ||
{ | ||
SetupBuilder(); | ||
var layout = new VerticalStackLayout(); | ||
var view = (View)Activator.CreateInstance(controlType); | ||
|
||
layout.Children.Add(view); | ||
|
||
await CreateHandlerAndAddToWindow<LayoutHandler>(layout, async (handler) => | ||
{ | ||
await OnLoadedAsync(view); | ||
view.Focus(); | ||
await AssertionExtensions.WaitForFocused(view); | ||
Assert.Contains(typeof(ResignFirstResponderTouchGestureRecognizer), | ||
handler.PlatformView.Window.GestureRecognizers.Select(x => x.GetType())); | ||
|
||
layout.Remove(view); | ||
await OnUnloadedAsync(view); | ||
|
||
Assert.DoesNotContain(typeof(ResignFirstResponderTouchGestureRecognizer), | ||
handler.PlatformView.Window.GestureRecognizers.Select(x => x.GetType())); | ||
}); | ||
} | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
123 changes: 123 additions & 0 deletions
123
src/Core/src/Platform/iOS/ResignFirstResponderTouchGestureRecognizer.cs
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,123 @@ | ||
using System; | ||
using System.Collections.Generic; | ||
using UIKit; | ||
|
||
namespace Microsoft.Maui.Platform | ||
{ | ||
internal class ResignFirstResponderTouchGestureRecognizer : UITapGestureRecognizer | ||
{ | ||
UIView? _targetView; | ||
Token? _token; | ||
|
||
public ResignFirstResponderTouchGestureRecognizer(UIView targetView) : | ||
base() | ||
{ | ||
ShouldRecognizeSimultaneously = (recognizer, gestureRecognizer) => true; | ||
ShouldReceiveTouch = OnShouldReceiveTouch; | ||
CancelsTouchesInView = false; | ||
DelaysTouchesEnded = false; | ||
DelaysTouchesBegan = false; | ||
|
||
_token = AddTarget((a) => | ||
{ | ||
if (a is ResignFirstResponderTouchGestureRecognizer gr && gr.State == UIGestureRecognizerState.Ended) | ||
{ | ||
gr.OnTapped(); | ||
} | ||
}); | ||
|
||
_targetView = targetView; | ||
} | ||
|
||
void OnTapped() | ||
{ | ||
if (_targetView?.IsFirstResponder == true) | ||
_targetView?.ResignFirstResponder(); | ||
|
||
Disconnect(); | ||
} | ||
|
||
internal void Disconnect() | ||
{ | ||
if (_token != null) | ||
RemoveTarget(_token); | ||
|
||
_token = null; | ||
_targetView = null; | ||
} | ||
|
||
bool OnShouldReceiveTouch(UIGestureRecognizer recognizer, UITouch touch) | ||
{ | ||
foreach (UIView v in ViewAndSuperviewsOfView(touch.View)) | ||
{ | ||
if (v != null && (v is UITableView || v is UITableViewCell || v.CanBecomeFirstResponder)) | ||
return false; | ||
} | ||
|
||
return true; | ||
} | ||
|
||
IEnumerable<UIView> ViewAndSuperviewsOfView(UIView view) | ||
{ | ||
while (view != null) | ||
{ | ||
yield return view; | ||
view = view.Superview; | ||
} | ||
} | ||
|
||
internal static void Update(UITextView textView, UIWindow? window) | ||
{ | ||
if (window != null) | ||
{ | ||
textView.Started += OnEditingDidBegin; | ||
textView.Ended += OnEditingDidEnd; | ||
} | ||
else | ||
{ | ||
textView.Started -= OnEditingDidBegin; | ||
textView.Ended -= OnEditingDidEnd; | ||
} | ||
} | ||
|
||
internal static void Update(UIControl platformControl, UIWindow? window) | ||
{ | ||
if (window != null) | ||
{ | ||
platformControl.EditingDidBegin += OnEditingDidBegin; | ||
platformControl.EditingDidEnd += OnEditingDidEnd; | ||
} | ||
else | ||
{ | ||
platformControl.EditingDidBegin -= OnEditingDidBegin; | ||
platformControl.EditingDidEnd -= OnEditingDidEnd; | ||
} | ||
} | ||
|
||
static void OnEditingDidBegin(object? sender, EventArgs e) | ||
{ | ||
if (sender is UIView view && view.Window != null) | ||
{ | ||
var resignFirstResponder = new ResignFirstResponderTouchGestureRecognizer(view); | ||
view.Window.AddGestureRecognizer(resignFirstResponder); | ||
return; | ||
} | ||
} | ||
|
||
static void OnEditingDidEnd(object? sender, EventArgs e) | ||
{ | ||
if (sender is UIView view && view.Window?.GestureRecognizers != null) | ||
{ | ||
for (var i = 0; i < view.Window.GestureRecognizers.Length; i++) | ||
{ | ||
UIGestureRecognizer? gr = view.Window.GestureRecognizers[i]; | ||
if (gr is ResignFirstResponderTouchGestureRecognizer) | ||
{ | ||
view.Window.RemoveGestureRecognizer(gr); | ||
return; | ||
} | ||
} | ||
} | ||
} | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,3 +1,7 @@ | ||
#nullable enable | ||
override Microsoft.Maui.Handlers.SwitchHandler.NeedsContainer.get -> bool | ||
static Microsoft.Maui.Layouts.LayoutExtensions.ArrangeContentUnbounded(this Microsoft.Maui.IContentView! contentView, Microsoft.Maui.Graphics.Rect bounds) -> Microsoft.Maui.Graphics.Size | ||
override Microsoft.Maui.Platform.MauiTextField.WillMoveToWindow(UIKit.UIWindow? window) -> void | ||
override Microsoft.Maui.Platform.MauiTextView.WillMoveToWindow(UIKit.UIWindow? window) -> void | ||
*REMOVED*override Microsoft.Maui.Handlers.PageHandler.ConnectHandler(Microsoft.Maui.Platform.ContentView! nativeView) -> void | ||
*REMOVED*override Microsoft.Maui.Handlers.PageHandler.DisconnectHandler(Microsoft.Maui.Platform.ContentView! nativeView) -> void |
4 changes: 4 additions & 0 deletions
4
src/Core/src/PublicAPI/net-maccatalyst/PublicAPI.Unshipped.txt
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,3 +1,7 @@ | ||
#nullable enable | ||
override Microsoft.Maui.Handlers.SwitchHandler.NeedsContainer.get -> bool | ||
static Microsoft.Maui.Layouts.LayoutExtensions.ArrangeContentUnbounded(this Microsoft.Maui.IContentView! contentView, Microsoft.Maui.Graphics.Rect bounds) -> Microsoft.Maui.Graphics.Size | ||
override Microsoft.Maui.Platform.MauiTextField.WillMoveToWindow(UIKit.UIWindow? window) -> void | ||
override Microsoft.Maui.Platform.MauiTextView.WillMoveToWindow(UIKit.UIWindow? window) -> void | ||
*REMOVED*override Microsoft.Maui.Handlers.PageHandler.ConnectHandler(Microsoft.Maui.Platform.ContentView! nativeView) -> void | ||
*REMOVED*override Microsoft.Maui.Handlers.PageHandler.DisconnectHandler(Microsoft.Maui.Platform.ContentView! nativeView) -> void |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.