fix: resolve grab points in the touch's window space on Android - #13
Merged
Conversation
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.
Fixes #12.
The problem
Fabric measures in the coordinate space of the surface root, while touches report
pageX/pageYin the coordinate space of the native window hosting that surface. The two coincide only when the surface root sits at the window origin — which is true on iOS, and false for Android's main window, where the surface starts a status bar below the window.Hit testing rebased page coordinates onto the owner's Fabric origin as if both were the same space, so on Android every selection in the main window landed one status bar height below the finger. Tapping the centre of "Open auto sheet" (y=1057px) selected "Open modal" (y=1199px) — 142px off, exactly
StatusBar.currentHeightat that density. It reproduced withReactNativeGrabRootalone, so it was never about native headers orReactNativeGrabScreen.The fix
Nothing is hardcoded and nothing is platform-conditional: the gap is read off the touch itself. A touch carries both coordinate spaces at once —
pageX/pageYin window space, pluslocationX/locationYrelative totarget, which measures in Fabric space — sogetFabricWindowOffsetcomputestargetOrigin + location - pageat runtime. On iOS that resolves to[0, 0], leaving the arithmetic identical to what shipped in #11. If the target tag cannot be resolved it degrades to[0, 0]as well.It is read once, when the gesture starts. The offset belongs to the window rather than to the touched view, and Android only guarantees that
locationis consistent withtargeton the initial touch, so recomputing it on every move would risk stale coordinates mid-drag.Because the offset is per-window rather than per-platform, natively presented surfaces — which get their own window, where the two spaces already agree — keep working. A global
StatusBar.currentHeightcorrection was tried first and rejected for exactly that reason: it fixed the main window and broke every sheet by the same amount.Verification
npm run lint,npm run format:check, and 18 unit tests including three new ones coveringgetFabricWindowOffset(aligned origins, offset origins, and the unresolvable-target fallback).Driven on device with agent-device.
Android (Pixel 8 emulator, API 35) — every case selects the element under the finger:
Text (in ScreenTarget)— previously 142px offText (in AutoSheetTarget)Text (in StackedSheetTarget)ModaltargetText (in ModalTarget)iOS (iPhone 17 Pro simulator) — regression pass over the same flows, each with the highlight box drawn exactly on the tapped text, all unchanged from before this fix: screen target, auto sheet, stacked sheet, dismissing the top sheet falling back to the sheet below, and the RN
Modal.