From ef731ffbb2f21ff9c1846619d519c8219eb2a7b8 Mon Sep 17 00:00:00 2001 From: Shai Almog <67850168+shai-almog@users.noreply.github.com> Date: Sun, 24 May 2026 07:22:45 +0300 Subject: [PATCH] Fix #5010: don't reroute taps on the active native text editor into CN1's pointer pipeline CN1TapGestureRecognizer is installed on the window with cancelsTouchesInView=NO, so it sees every touch in the window -- including taps that land on the CN1UITextField / CN1UITextView created by editStringAtImpl. The shouldBeRequiredToFailByGestureRecognizer: gate added in #5003 made CN1TapGR win priority over the iOS-26 auto-installed _keyboardDismissalGestureRecognized: tap, which (combined with the always-on foldKeyboard call in touchesEnded) turned every tap inside the field into: CN1TapGR.touchesEnded -> [ctrl foldKeyboard:point] -> point falls outside the 22pt-tall field rect for a single-line TextField almost any time the user isn't pixel-perfect (cursor positioning drag, long-press for the magnifier, even a slightly off-center tap) -> stringEdit(YES,...) tears down editingComponent -> pointerReleasedC dispatches the same touch into Java -> TextArea.pointerReleased -> editString -> editStringAtImpl re-creates the field The visible symptom (#5010 follow-ups, captured in the on-device trace): the keyboard bounces, the selection / cursor jumps back to the default position on every interaction, and on iOS 26.x devices the rapid session churn fails the RTI UIEmojiSearchOperations probe -- which on iPhone 14 Plus / iOS 26.4.2 escalates to a full-app freeze. Fix: extend ignoreEvent: to ignore touches whose pressedView is a descendant of editingComponent. UIKit owns those touches for cursor positioning, selection handles, the magnifier loupe, and on iOS 26.x the RTI / emoji- search input session. CN1TapGR still processes everything else (taps on peer components, taps on the empty form area, taps that should dismiss the keyboard via foldKeyboard). Verified on iPhone 17 Pro / iOS 26.x build of the AddressBook repro (single TextField in BorderLayout.CENTER, the snippet from the issue report): typing works straight through, the keyboard no longer bounces on tap, cursor positioning lands where the user taps, and the RTI emoji- search errors stop firing entirely. Co-Authored-By: Claude Opus 4.7 (1M context) --- .../nativeSources/CN1TapGestureRecognizer.m | 21 ++++++++++++++++--- 1 file changed, 18 insertions(+), 3 deletions(-) diff --git a/Ports/iOSPort/nativeSources/CN1TapGestureRecognizer.m b/Ports/iOSPort/nativeSources/CN1TapGestureRecognizer.m index 21edcea83a..2ef323540b 100644 --- a/Ports/iOSPort/nativeSources/CN1TapGestureRecognizer.m +++ b/Ports/iOSPort/nativeSources/CN1TapGestureRecognizer.m @@ -91,14 +91,29 @@ -(BOOL)ignoreEvent:(UITouch*)touch { // We DO NOT want to process touches from popovers like datepickers and openGallery. // See the OpenGalleryTest2793 sample to test events for openGallery. UIView *v = ctrl.view; - + // Sometimes we receive an event from a view that has already been removed from // the view hierarchy. The call to [pressedView isDescendantOfView:xxx] will throw // a EXC_BAD_ACCESS in this case, so we need to test for this case. BOOL viewInWindow = pressedView != nil && [pressedView window] != nil; - + BOOL ignore = (touch == nil || pressedView == nil || !viewInWindow || ![pressedView isDescendantOfView:v]); + // Touches that land on the active native text editor (the CN1UITextField / + // CN1UITextView created by editStringAtImpl) must be left to UIKit. They + // drive cursor positioning, selection handles, the magnifier loupe, and on + // iOS 26.x the RTI / emoji-search input session. If we dispatch them into + // CN1's pointer pipeline, touchesEnded fires foldKeyboard -- whose + // 22pt-tall hit-test rect rejects most taps on a single-line TextField -- + // which calls stringEdit(YES,...), tears down editingComponent, and lets + // the same touch's pointerReleasedC re-enter Java's TextArea.pointerReleased, + // which recreates the field. The visible symptom is the keyboard bouncing + // and the selection / cursor resetting on every interaction; see #5010. + if (!ignore && editingComponent != nil && pressedView != nil + && [pressedView isDescendantOfView:editingComponent]) { + ignore = YES; + } + return ignore; } @@ -214,7 +229,7 @@ - (void) touchesMoved:(NSSet *)touches withEvent:(UIEvent *)event - (void) touchesEnded:(NSSet *)touches withEvent:(UIEvent *)event { - + [super touchesEnded:touches withEvent:event]; if(skipNextTouch) { skipNextTouch = NO;