diff --git a/Ports/iOSPort/nativeSources/CN1TapGestureRecognizer.m b/Ports/iOSPort/nativeSources/CN1TapGestureRecognizer.m index 2ef323540b..ec8985245a 100644 --- a/Ports/iOSPort/nativeSources/CN1TapGestureRecognizer.m +++ b/Ports/iOSPort/nativeSources/CN1TapGestureRecognizer.m @@ -99,21 +99,6 @@ -(BOOL)ignoreEvent:(UITouch*)touch { 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; } diff --git a/Ports/iOSPort/nativeSources/CodenameOne_GLViewController.m b/Ports/iOSPort/nativeSources/CodenameOne_GLViewController.m index db36514c75..4942a3cd56 100644 --- a/Ports/iOSPort/nativeSources/CodenameOne_GLViewController.m +++ b/Ports/iOSPort/nativeSources/CodenameOne_GLViewController.m @@ -538,10 +538,7 @@ void cn1_setStyleDoneButton(CN1_THREAD_STATE_MULTI_ARG UIBarButtonItem* btn) { if(isIOS8() && displayHeight < displayWidth) { showToolbar = NO; } - // #5010 diagnostic: bounded one-line marker so device logs can correlate - // "edit session started" against the press / text-input traces. - CN1Log(@"[#5010] editStringAtImpl ENTRY isSingleLine=%d maxSize=%d constraint=%d initialLen=%d existingEditing=%p", - isSingleLine, maxSize, constraint, len, editingComponent); + //CN1Log(@"Java_com_codename1_impl_ios_IOSImplementation_editStringAtImpl"); currentlyEditingMaxLength = maxSize; // Honored by the UITextView shouldChangeTextInRange: delegate (EAGLView/METALView) to // intercept Return on multi-line text areas when the iosReturnExitsEditing client @@ -2704,27 +2701,7 @@ - (void)remoteControlReceivedWithEvent:(UIEvent *)receivedEvent { // Mac Catalyst host keyboard, hardware keyboard in the iOS simulator via // Cmd-Shift-K). UIKey arrived in iOS 13.4 -- on older versions the // responder chain falls back to the existing UITextField editing path. -// -// While a native text editor is up (editingComponent != nil) we must not -// touch the press at all -- not even forward to [super pressesBegan:]. -// The CN1UITextField is the first responder and handles every printable -// key via its own pressesBegan: / insertText: pipeline before the event -// walks up to us. Forwarding to [super pressesBegan:] re-enters -// UIViewController's default chain walk, which on iPhone / iOS 26.4.2 -// hangs the next inbound key delivery (the user sees a focused field -// where typing does nothing and the rest of the app freezes -- see -// #5010). Returning early keeps the override fully transparent while -// editing and preserves HW-keyboard support (#3498) for non-editing -// state. The keyboard NSLog calls are intentionally left in for #5010 -// diagnostics; volume is bounded because pressesBegan only fires on -// physical key events. - (void)pressesBegan:(NSSet *)presses withEvent:(UIPressesEvent *)event { - if (editingComponent != nil) { - CN1Log(@"[#5010] pressesBegan SKIPPED while editing (count=%lu editingComponent=%p)", - (unsigned long)presses.count, editingComponent); - return; - } - CN1Log(@"[#5010] pressesBegan handling (count=%lu)", (unsigned long)presses.count); if (@available(iOS 13.4, *)) { BOOL handled = NO; NSMutableSet *passthrough = nil; @@ -2755,12 +2732,6 @@ - (void)pressesBegan:(NSSet *)presses withEvent:(UIPressesEvent *)eve } - (void)pressesEnded:(NSSet *)presses withEvent:(UIPressesEvent *)event { - if (editingComponent != nil) { - CN1Log(@"[#5010] pressesEnded SKIPPED while editing (count=%lu editingComponent=%p)", - (unsigned long)presses.count, editingComponent); - return; - } - CN1Log(@"[#5010] pressesEnded handling (count=%lu)", (unsigned long)presses.count); if (@available(iOS 13.4, *)) { BOOL handled = NO; NSMutableSet *passthrough = nil; @@ -2791,12 +2762,6 @@ - (void)pressesEnded:(NSSet *)presses withEvent:(UIPressesEvent *)eve } - (void)pressesCancelled:(NSSet *)presses withEvent:(UIPressesEvent *)event { - if (editingComponent != nil) { - CN1Log(@"[#5010] pressesCancelled SKIPPED while editing (count=%lu editingComponent=%p)", - (unsigned long)presses.count, editingComponent); - return; - } - CN1Log(@"[#5010] pressesCancelled handling (count=%lu)", (unsigned long)presses.count); if (@available(iOS 13.4, *)) { for (UIPress *press in presses) { UIKey *key = press.key; diff --git a/scripts/input-validation-app/README.adoc b/scripts/input-validation-app/README.adoc index 6fc71c1be4..c27929fd81 100644 --- a/scripts/input-validation-app/README.adoc +++ b/scripts/input-validation-app/README.adoc @@ -1,8 +1,8 @@ = CN1 Input Validation App A minimal Codename One app whose only purpose is to assert that physical -input events (tap, drag, long-press, keyboard) reach Component listeners -end-to-end on iOS. Driven by XCUITest on the iOS simulator. +input events (tap, drag, long-press) reach Component listeners end-to-end +on iOS. Driven by XCUITest on the iOS simulator. == Why a separate pipeline? @@ -44,15 +44,6 @@ the expected time. | `addLongPressListener` fires for a press-and-hold. Routes through the same touch chain as tap, so a recognizer that cancels touches mid-press causes this step to time out. - -| `keytype` -| Typed characters reach a CN1 `TextField.DataChangedListener`. The - driver taps the field to bring up native iOS editing, then synthesises - keystrokes via `XCUIApplication.typeText`. typeText drives the - simulator's HW-keyboard pathway, which on iOS 13.4+ surfaces as - `UIPress` events through `GLViewController` -- the path #5010 broke - by treating every printable key as "handled" and never forwarding it - to UIKit's text-input pipeline. |=== == Log markers diff --git a/scripts/input-validation-app/common/src/main/java/com/codenameone/inputvalidation/gestures/GestureSuite.java b/scripts/input-validation-app/common/src/main/java/com/codenameone/inputvalidation/gestures/GestureSuite.java index 70c78a9825..1b58d95cef 100644 --- a/scripts/input-validation-app/common/src/main/java/com/codenameone/inputvalidation/gestures/GestureSuite.java +++ b/scripts/input-validation-app/common/src/main/java/com/codenameone/inputvalidation/gestures/GestureSuite.java @@ -39,17 +39,6 @@ public GestureSuite() { new TapStep(), new DragStep(), new LongPressStep() - // KeyTypeStep is disabled pending #5010 resolution. The - // step exercises the HW-keyboard UIPress chain (typeText in - // XCUITest), which depends on [super pressesBegan:] being - // forwarded from CodenameOne_GLViewController while a field - // is being edited. The iOS-26.4.2 freeze investigation - // points to that same forwarding as the trigger, so the - // override now returns early without forwarding -- which - // breaks this step on the simulator. Re-enable once the - // freeze root cause and a fix that preserves both paths - // are landed. - //, new KeyTypeStep() }; this.form = new Form("Input Validation", new BorderLayout()); this.statusLabel = new Label("Initializing"); diff --git a/scripts/input-validation-app/common/src/main/java/com/codenameone/inputvalidation/gestures/KeyTypeStep.java b/scripts/input-validation-app/common/src/main/java/com/codenameone/inputvalidation/gestures/KeyTypeStep.java deleted file mode 100644 index 28bbed5a53..0000000000 --- a/scripts/input-validation-app/common/src/main/java/com/codenameone/inputvalidation/gestures/KeyTypeStep.java +++ /dev/null @@ -1,68 +0,0 @@ -/* - * Copyright (c) 2026, Codename One and/or its affiliates. All rights reserved. - * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. - */ -package com.codenameone.inputvalidation.gestures; - -import com.codename1.ui.Container; -import com.codename1.ui.Font; -import com.codename1.ui.TextArea; -import com.codename1.ui.TextField; -import com.codename1.ui.layouts.BorderLayout; - -/// Validates that keyboard input lands in a CN1 TextField end-to-end. -/// The XCUITest driver taps the field to bring up native iOS editing -/// (CN1UITextField becomes first responder), then synthesises keystrokes -/// via `XCUIApplication.typeText`. typeText drives the simulator's -/// hardware-keyboard pathway, which on iOS 13.4+ surfaces as UIPress -/// events that walk the responder chain through `GLViewController`. -/// -/// Regression coverage for #5010: a pressesBegan: handler in -/// CodenameOne_GLViewController.m treated every UIPress whose UIKey -/// mapped to a non-zero CN1 keycode as consumed -- and printable -/// characters map to their unicode codepoint, which is always non-zero. -/// That swallowed every HW keystroke before UIKit could convert it into -/// insertText: on the focused CN1UITextField, and on iOS 26.x devices -/// the same path also swallowed virtual-keyboard input. The fix bypasses -/// the intercept while editingComponent != nil. This step fails to -/// receive EXPECTED_TEXT and times out if either bug ever returns. -public final class KeyTypeStep implements GestureStep { - /// XCUITest types this exact string. Kept short, lowercase, and not a - /// dictionary word so iOS auto-capitalisation / auto-correct cannot - /// silently rewrite the characters before the CN1 TextField sees them. - public static final String EXPECTED_TEXT = "cn1"; - - @Override - public String name() { - return "keytype"; - } - - @Override - public void install(Container target, Callback callback) { - // Disable predictive text in the constraint so simulated keystrokes - // land verbatim. Without this iOS auto-capitalisation can rewrite - // the first character (`Cn1` instead of `cn1`) and make the - // assertion brittle across keyboard configurations. - final TextField field = new TextField("", "Type " + EXPECTED_TEXT + " here", - EXPECTED_TEXT.length() + 8, - TextArea.ANY | TextArea.NON_PREDICTIVE); - field.setName("cn1iv-keytype-target"); - // Match TapStep / LongPressStep tap-target sizing so the XCUITest - // driver can use the same (0.5, 0.5) coordinate to focus the - // field on every iPhone size class on the CI runner. A NORTH - // placement put the field above where the existing steps tap - // and the driver missed it -- see #5010 CI failure. - field.getAllStyles().setFont(Font.createSystemFont(Font.FACE_SYSTEM, Font.STYLE_BOLD, Font.SIZE_LARGE)); - field.getAllStyles().setPadding(48, 48, 48, 48); - field.getAllStyles().setMargin(48, 48, 48, 48); - final boolean[] fired = {false}; - field.addDataChangedListener((type, index) -> { - String text = field.getText(); - if (!fired[0] && text != null && text.contains(EXPECTED_TEXT)) { - fired[0] = true; - callback.onDetected("text=" + text); - } - }); - target.add(BorderLayout.CENTER, field); - } -} diff --git a/scripts/input-validation-app/drivers/run-ios.sh b/scripts/input-validation-app/drivers/run-ios.sh index 52eeb4af8d..22a0ec14af 100755 --- a/scripts/input-validation-app/drivers/run-ios.sh +++ b/scripts/input-validation-app/drivers/run-ios.sh @@ -187,13 +187,6 @@ REQUIRED_EVENTS=( "CN1IV:EVENT:drag" "CN1IV:READY:longpress" "CN1IV:EVENT:longpress" - # keytype is disabled pending #5010 resolution -- see GestureSuite.java - # and ios-tests/Sources/InputValidationUITests.swift. Re-enable both - # CN1IV:READY:keytype and CN1IV:EVENT:keytype together when a fix - # that preserves both the HW-keyboard UIPress chain and the iOS-26.4.2 - # virtual-keyboard freeze fix has landed. - # "CN1IV:READY:keytype" - # "CN1IV:EVENT:keytype" "CN1IV:SUITE:FINISHED" ) FAILED=0 diff --git a/scripts/input-validation-app/ios-tests/Sources/InputValidationUITests.swift b/scripts/input-validation-app/ios-tests/Sources/InputValidationUITests.swift index a4012f96e3..725871a2a4 100644 --- a/scripts/input-validation-app/ios-tests/Sources/InputValidationUITests.swift +++ b/scripts/input-validation-app/ios-tests/Sources/InputValidationUITests.swift @@ -52,15 +52,6 @@ final class InputValidationUITests: XCTestCase { try driveLongPress(app: app) Thread.sleep(forTimeInterval: stepDelaySeconds) - - // KeyTypeStep is disabled pending #5010 resolution. See the - // matching note in GestureSuite.java and the removed assertion in - // drivers/run-ios.sh. The driveKeyType helper is intentionally - // kept below so the step is one-line re-enable once a fix that - // preserves both the HW-keyboard UIPress chain and the iOS-26.4.2 - // virtual-keyboard freeze fix has landed. - // try driveKeyType(app: app) - // Thread.sleep(forTimeInterval: stepDelaySeconds) } private func driveTap(app: XCUIApplication) throws { @@ -80,21 +71,4 @@ final class InputValidationUITests: XCTestCase { let center = app.coordinate(withNormalizedOffset: CGVector(dx: 0.5, dy: 0.5)) center.press(forDuration: 1.5) } - - private func driveKeyType(app: XCUIApplication) throws { - // KeyTypeStep places its TextField in BorderLayout.CENTER with - // generous padding/margin, matching the layout TapStep and - // LongPressStep use so a single screen-center tap focuses it on - // every iPhone size class on the CI runner. Wait briefly after - // the tap for CN1's editStringAtImpl to install the native - // CN1UITextField and animate the keyboard in -- typeText raises - // "Neither element nor any descendant has keyboard focus" if no - // first responder accepts input yet -- then type. typeText - // synthesises HW-keyboard UIPress events that walk through - // GLViewController, exactly the path #5010 broke. - let center = app.coordinate(withNormalizedOffset: CGVector(dx: 0.5, dy: 0.5)) - center.tap() - Thread.sleep(forTimeInterval: 2.0) - app.typeText("cn1") - } }