Fix iOS 26 tap regressions + ParparVM BGPainter NPE#5003
Merged
shai-almog merged 1 commit intoMay 22, 2026
Conversation
Three independent fixes surfaced while testing on iOS 26.3 simulator under Xcode 26: 1. Component.animate(): replace `getClass() != BGPainter.class` with `!(bgp instanceof BGPainter)`. The class-literal comparison was being bypassed when bgp was a Component$BGPainter constructed via the no-arg / Style / Painter constructors (motion fields null), so the Form-as-animation path ran `bgp.animate()` and tripped a NullPointerException on `wMotion.isFinished()` ~600ms after every form show. instanceof is the intended check anyway -- BGPainter has no subclasses. 2. CN1TapGestureRecognizer: implement `shouldBeRequiredToFailByGestureRecognizer:` to defeat the `_keyboardDismissalGestureRecognized:`-target UITapGestureRecognizer that iOS 26 auto-installs on every UIWindow. Without this, that window-level recognizer (cancelsTouchesInView=YES) consumed tap-down events before our recognizer's touchesBegan: ran, so all taps were dropped. 3. UIHoverGestureRecognizer on the GL view: explicitly clear cancelsTouchesInView/delaysTouchesBegan/delaysTouchesEnded. The UIGestureRecognizer defaults to cancelsTouchesInView=YES, and on simulator builds the host mac cursor is always hovering over the window, so the hover recognizer was preempting touches. Verified end-to-end on iPhone 17 Pro / iOS 26.3 simulator: form renders, tap fires pointerPressed + Button.actionPerformed, no recurring NPE in the EDT loop.
Contributor
✅ Continuous Quality ReportTest & Coverage
Static Analysis
Generated automatically by the PR CI workflow. |
Collaborator
Author
|
Compared 110 screenshots: 110 matched. Native Android coverage
✅ Native Android screenshot tests passed. Native Android coverage
Benchmark ResultsDetailed Performance Metrics
|
Collaborator
Author
Collaborator
Author
|
Compared 110 screenshots: 110 matched. Benchmark Results
Build and Run Timing
Detailed Performance Metrics
|
4 tasks
shai-almog
added a commit
that referenced
this pull request
May 22, 2026
iOS build was failing because IOSSimd.m uses ARM NEON intrinsics guarded by `#if defined(__aarch64__)` and a `generic/platform=iOS Simulator` destination triggers a universal build whose x86_64 slice has no `uint32x4_t` etc. Mirror what run-ios-ui-tests.sh does for hellocodenameone: detect host arch via `uname -m`, force `ARCHS=$HOST_ARCH ONLY_ACTIVE_ARCH=YES` and pin `-sdk iphonesimulator -configuration Debug`. JS Playwright driver: `page.mouse.click` was missing because the CN1 JS port registers pointer listeners on a sibling `<div id="cn1-peers-container">`, not on `<canvas id="codenameone-canvas">` (which has `pointer-events:none`). Page-level clicks land on body and never reach the listener. Dispatch synthetic `MouseEvent`s on `#cn1-peers-container` via `page.evaluate` so the CN1 dispatcher actually sees mousedown / mousemove / mouseup -- still exercises every CN1 listener that a PR #5003-class bug would break. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
shai-almog
added a commit
that referenced
this pull request
May 23, 2026
…5005) * Add input-validation pipeline: XCUITest + Playwright gesture suite Scaffolds a minimal CN1 app under scripts/input-validation-app whose only job is to assert that physical taps, drags, and long-presses reach Component listeners end-to-end on each port. Driven by OS-level automation (XCUITest on iOS, Playwright on JavaScript) and asserted via a structured CN1IV:EVENT:* log stream -- no screenshots, no chunked Base64, no reference comparisons. Closes the test-coverage gap exposed by PR #5003: three independent input-chain regressions shipped on iOS 26 because the existing hellocodenameone suite builds every form programmatically from runTest() and never depends on a touch event actually firing. CI workflow is scaffolded but gated `if: false` pending generalisation of scripts/build-ios-app.sh (currently hard-coded to the hellocodenameone module name); manual local runs work today per the README. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> * Remove unused `missing` variable in Playwright driver The misses are already reported by the for-loop over REQUIRED_EVENTS below, so the precomputed array was dead code. Flagged by github-code-quality on PR #5005. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> * Wire input-validation iOS + JS jobs into actual CI runs Generalise the two build scripts via CN1_APP_DIR / CN1_APP_MAIN_NAME / CN1_APP_PACKAGE_NAME env overrides -- defaults stay pointed at hellocodenameone so the existing scripts-ios.yml / scripts-javascript.yml pipelines remain unchanged: * scripts/build-ios-app.sh: read APP_MAIN_NAME from codenameone_settings.properties; templated workspace, scheme and Xcode project names off it so any CN1 app can be built. * scripts/build-javascript-port-hellocodenameone.sh: read packageName/mainName from settings, parameterise the generated JavaScript launcher and the ByteCodeTranslator invocation. Add the input-validation app's CN1 project metadata so the CN1 plugin knows what to name everything: codenameone_settings.properties (mainName= InputValidationApp, packageName=com.codenameone.inputvalidation), a stub icon.png copied from hellocodenameone, and the Maven wrapper alongside. Rewrite .github/workflows/input-validation.yml so both jobs actually run rather than being gated by `if: false`: * `build-port` (reusable from scripts-ios.yml) builds + caches the CN1 iOS port. * `ios` consumes that cache, runs build-ios-app.sh with CN1_APP_DIR= scripts/input-validation-app, builds the .app via xcodebuild for the simulator, and hands it to drivers/run-ios.sh which drives gestures via XCUITest and asserts CN1IV:* lines in the os_log stream. * `javascript` builds the ParparVM browser bundle, serves it from python3 -m http.server, and drives Chromium via drivers/run-js.sh. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> * Fix iOS arch mismatch + dispatch JS events on peersContainer iOS build was failing because IOSSimd.m uses ARM NEON intrinsics guarded by `#if defined(__aarch64__)` and a `generic/platform=iOS Simulator` destination triggers a universal build whose x86_64 slice has no `uint32x4_t` etc. Mirror what run-ios-ui-tests.sh does for hellocodenameone: detect host arch via `uname -m`, force `ARCHS=$HOST_ARCH ONLY_ACTIVE_ARCH=YES` and pin `-sdk iphonesimulator -configuration Debug`. JS Playwright driver: `page.mouse.click` was missing because the CN1 JS port registers pointer listeners on a sibling `<div id="cn1-peers-container">`, not on `<canvas id="codenameone-canvas">` (which has `pointer-events:none`). Page-level clicks land on body and never reach the listener. Dispatch synthetic `MouseEvent`s on `#cn1-peers-container` via `page.evaluate` so the CN1 dispatcher actually sees mousedown / mousemove / mouseup -- still exercises every CN1 listener that a PR #5003-class bug would break. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> * Pin iOS sim by availability; broaden JS dispatch to touch + pointer iOS: previous run-ios.sh hard-coded `iPhone 17 Pro` which only exists on Xcode 26 -- the macos-15 runner default Xcode is 16.4, top device is iPhone 16. Auto-pick the newest available iPhone runtime and fall back through the list if the user-supplied CN1IV_DEVICE_NAME isn't installed. JS Playwright driver: add a layout pre-flight that fires its own mousedown probe on `#cn1-peers-container` to confirm dispatchEvent actually reaches the listener (rules out "synthetic events are silently dropped" before we blame CN1). Also fire PointerEvent + TouchEvent variants alongside MouseEvent in case the JS port's pointer dispatcher only listens to one channel. Same gesture flow, just multi-modal. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> * Mark input-validation jobs advisory; capture iOS xcresult Both jobs run, build, install, launch -- but the actual gesture-dispatch endpoints turn out to need port-side work: * iOS: XCUITest application-only mode (no host-app) errors with the opaque `** TEST FAILED **` under Xcode 16.4. The runner build + simctl install + log-stream path is verified. Capture the .xcresult and run xcresulttool against it so the artifact has the real failure reason for post-hoc diagnosis rather than a generic xcodebuild line. * JavaScript: the layout dump shows synthetic mousedown reaches main- thread listeners we add (`selfTestFired:true`) but the CN1 JS port registers its pointer listeners through a host-bridge proxy from inside a Web Worker; synthetic events don't traverse that proxy the way real OS events do. End-to-end input validation here needs JS-port-side work. Mark both jobs `continue-on-error: true` so the infrastructure lands without gating PRs on issues that are about the ports, not about this pipeline. README updated with the current state. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> * Drop JS job; fix iOS XCUITest with a host-app stub Two changes: 1. Drop the JavaScript job entirely. The CN1 JS port registers its pointer listeners through a host-bridge proxy from inside a Web Worker; synthetic browser events on the main thread don't traverse that proxy the way real OS events do. The diagnostic from the previous run confirms this (the driver's layout self-test fires its own listener -- `selfTestFired:true` -- but CN1's listeners never fire). End-to-end input validation on JS needs port-side changes that don't belong in this PR. Removing the job, the JS module, the Playwright driver, and reverting the JS build-script generalisation keeps the diff focused. 2. Fix the iOS XCUITest by giving it a proper host-app target. Application-only UI testing (TEST_TARGET_NAME="" + USES_XCTRUNNER) was exiting with an opaque `** TEST FAILED **` under Xcode 16.4 -- that mode is finicky and not how the standard XCUITest pipeline works. The XcodeGen project now declares a minimal `HostStub` target (a UIApplicationMain that does nothing) and points TEST_TARGET_NAME at it. The UI test code is unchanged -- it still attaches to the already-installed CN1 app by bundle id via `XCUIApplication(bundleIdentifier:)`. The stub is purely there so the XCUITest runner has a regular host to launch into. `continue-on-error: true` removed -- it was masking failure, not fixing anything. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> * Fix iOS XCUITest: correct bundle id, drop misleading env var The xcresult artifact from the previous run pinpointed the actual failure: "Failed to launch com.codenameone.examples.inputvalidation". The Swift fallback had `examples.` baked in but the CN1 maven plugin derives CFBundleIdentifier straight from `codename1.packageName`, which is `com.codenameone.inputvalidation` (no `examples.`) per common/codenameone_settings.properties. The CN1IV_BUNDLE_ID env override was also broken: `xcodebuild test KEY=VALUE` sets a *build setting*, not a runtime environment variable for the test process. The Swift code's ProcessInfo lookup would never see it, so the fallback was always used. Hard-code the correct id; drop the broken env var passing; warn from run-ios.sh if the installed bundle id doesn't match what the test will request. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
shai-almog
added a commit
that referenced
this pull request
May 23, 2026
…omponent (#5013) * Fix #5010: don't swallow UIPress events while editing a native text component The hardware-keyboard handler in CodenameOne_GLViewController consumed every UIPress whose UIKey mapped to a non-zero CN1 keycode -- and for printable characters cn1MapUIKeyToKeyCode returns the unicode codepoint, which is always non-zero. With handled=YES the press was never forwarded to [super pressesBegan:], so UIKit's text-input pipeline never converted it into insertText: on the focused CN1UITextField / CN1UITextView. That broke hardware-keyboard typing outright on iOS 13.4+ (BT keyboards, Magic Keyboard, simulator HW keyboard) the moment a text field was being edited. On iOS 26.x devices, where some on-screen keyboard interactions also reach the responder chain as UIPress events, the same path also swallowed virtual-keyboard typing -- the reporter's "cursor blinks but typing does nothing" symptom in #5010. Bypass the intercept entirely while editingComponent != nil. CN1 KeyEvent listeners legitimately stop firing during text editing, which matches the intuition that arrow keys etc. should move the caret rather than trigger app-level navigation while a field is focused. Verified on iPhone 17 Pro / iOS 26.3 simulator: with a single TextField focused, typing on the Mac host keyboard now lands characters in the field. Prior to the fix the same keystrokes were silently dropped. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> * Input-validation: add keytype step (XCUITest coverage for #5010) Extends the input-validation pipeline with a fourth gesture step that proves keyboard input lands in a CN1 TextField. The driver taps the field to bring up native iOS editing, then synthesises keystrokes via XCUIApplication.typeText -- which routes through the simulator's HW-keyboard pathway, raising UIPress events that walk through CodenameOne_GLViewController on iOS 13.4+. Without the companion fix in CodenameOne_GLViewController.m, every printable keystroke is consumed by pressesBegan: before UIKit converts it into insertText: on the focused CN1UITextField, the DataChangedListener never fires, and this step times out. With the fix, "cn1" propagates end-to-end through CN1UITextField -> UITextFieldTextDidChangeNotification -> EAGLView.textFieldDidChange -> stringEdit -> TextField.setText -> DataChangedListener and the step asserts CN1IV:EVENT:keytype. The pipeline already covers #5003's tap/drag/longpress regressions along the same touch chain; #5010 sits one layer deeper on the responder chain (UIPress instead of UITouch) and needs its own probe. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> * Input-validation: fix keytype tap-target placement so XCUITest hits it The first CI run of the new keytype step timed out: XCUITest threw "Neither element nor any descendant has keyboard focus" from `app.typeText("cn1")` because the (0.5, 0.18) tap landed in empty space below the TextField. The field had been added to BorderLayout.NORTH of the target area, which on the CI iPhone SE runner put it well above the y=0.18 band the driver tapped, so the field never entered editing mode and no first responder was available for typeText to write into. Switch the layout to match TapStep / LongPressStep: TextField in BorderLayout.CENTER with the same padding+margin sizing, tapped at the centered (0.5, 0.5) coordinate the other steps already use successfully. Also bumps the post-tap sleep to 2.0s for the slower boot of the CI simulator's keyboard animation. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This file contains hidden or 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
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.

Summary
Three independent regressions that surfaced while testing on iPhone 17 Pro / iOS 26.3 simulator under Xcode 26:
ParparVM
Component$BGPainter.animateNPE —Component.animate()was usingbgp.getClass() != BGPainter.classto skip the default background painter. That class-literal comparison failed under ParparVM and the path ran on aBGPainterconstructed via the no-arg /Style/Painterconstructors (motion fields are null in those), tripping aNullPointerExceptiononwMotion.isFinished()inComponent$BGPainter.animate()~600 ms after every form show. Replaced with!(bgp instanceof BGPainter), which is the intended check anyway --BGPainterhas no subclasses.iOS 26 keyboard-dismissal
UITapGestureRecognizerate every touch — iOS 26 auto-installs a window-levelUITapGestureRecognizerwhose action is_keyboardDismissalGestureRecognized:and whose defaultcancelsTouchesInView=YES. It runs ahead ofCN1TapGestureRecognizerin the recognizer chain, so tap-down events were consumed before ourtouchesBegan:ran. ImplementedshouldBeRequiredToFailByGestureRecognizer:onCN1TapGestureRecognizerso any window-levelUITapGestureRecognizermust wait for ours to fail.UIHoverGestureRecognizerpreempted touches on the simulator —UIGestureRecognizer.cancelsTouchesInViewdefaults to YES. In the simulator the host-mac cursor is always hovering over the window, so the hover recognizer was cancelling taps. Explicitly setcancelsTouchesInView=NO/delaysTouchesBegan=NO/delaysTouchesEnded=NOon the hover recognizer.Net effect: form renders, tap fires
pointerPressed+Button.actionPerformed, no recurring NPE in the EDT loop.Test plan
pointerPressedandactionPerformedshould fireant test-javase) — theComponent.animatechange is the only Java edit and is a logically equivalent rewrite🤖 Generated with Claude Code