iOS: Don't eat touch event when detecting edit menu tap gesture

Our logic in the edit menu tap recognizer for determining whether the
tap should trigger the edit menu only looks at the bounds of the input
area, and as long as the tap is within these bounds we trigger the edit
menu.

This doesn't take into account that there might be controls laid on top
of the input area, for example a button, where we don't want to trigger
the gesture.

The only related logic we have is a heuristic that checks whether the
cursor ends up moving as a result of the initial press, in which case
we treat it as the edit area "accepting" the touch press.

The proper fix to this is to handle the gesture recognizer delegate
callback gestureRecognizer:shouldReceiveTouch:, and do hit testing
there, but we don't have any machinery for that in our IM protocol,
nor in QWSI.

As a workaround, we treat the gesture recognizer as having failed,
even when we do detect a tap that should open the menu, so that the
recognizer doesn't eat the touch event. We then open the menu manually
instead of relying on the gesture recognizer changing state. This
should be safe, as sending a touch/mouse event down to the input
area should normally be a noop.

The workaround does result in a slight visual wart when clicking
buttons that live inside input areas, as the edit menu temporarily
opens, but the menu is quickly hidden again as the focus object is
transferred to the button. Compared to the current situation, where
the button is visually pressed, but doesn't do anything, and we
bring up the edit menu fully, this is an improvement, even with
the wart.

Note that the original problem outlined in this changed is not an
issue in practice for Qt Widgets, as we synthesize mouse events
from touch events, and (wrongly) synthesize a mouse release in
response to the touch cancel triggered by the gesture recognizer,
so the button is still activated.

Fixes: QTBUG-113975
Change-Id: I41e850f20d69ad8e3247949644a389e1c61c4dcf
Reviewed-by: Richard Moe Gustavsen <richard.gustavsen@qt.io>
Reviewed-by: Volker Hilsheimer <volker.hilsheimer@qt.io>
(cherry picked from commit 843ce2759ea1698171cc4c5fad64d8cc0690cea8)
Reviewed-by: Qt Cherry-pick Bot <cherrypick_bot@qt-project.org>
This commit is contained in:
Tor Arne Vestbø 2023-06-20 12:10:44 +02:00 committed by Qt Cherry-pick Bot
parent ec39297898
commit 33402cdf69

View File

@ -947,7 +947,17 @@ static void executeBlockWithoutAnimation(Block block)
int cursorPosOnRelease = QPlatformInputContext::queryFocusObject(Qt::ImCursorPosition, touchPos).toInt();
if (cursorPosOnRelease == _cursorPosOnPress) {
// We've recognized a gesture to open the menu, but we don't know
// whether the user tapped a control that was overlaid our input
// area, since we don't do any granular hit-testing in touchesBegan.
// To ensure that the gesture doesn't eat touch events that should
// have reached another UI control we report the gesture as failed
// here, and then manually show the menu at the next runloop pass.
_menuShouldBeVisible = true;
self.state = UIGestureRecognizerStateFailed;
dispatch_async(dispatch_get_main_queue(), ^{
QIOSTextInputOverlay::s_editMenu.visible = _menuShouldBeVisible;
});
} else {
// The menu is hidden, and the cursor will change position once
// Qt receive the touch release. We therefore fail so that we