Skip to content

fix: unable to move cursor in absolutely positioned TextInput on Android when parent View has zIndex #52290

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 1 commit into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -8,16 +8,19 @@
package com.facebook.react.uimanager

import android.view.MotionEvent
import android.view.View
import android.view.ViewGroup
import com.facebook.common.logging.FLog
import com.facebook.infer.annotation.Assertions
import com.facebook.react.bridge.ReactContext
import com.facebook.react.common.ReactConstants
import com.facebook.react.uimanager.TouchTargetHelper.findTouchTargetViewWithPointerEvents
import com.facebook.react.uimanager.common.UIManagerType
import com.facebook.react.uimanager.events.EventDispatcher
import com.facebook.react.uimanager.events.TouchEvent
import com.facebook.react.uimanager.events.TouchEventCoalescingKeyHelper
import com.facebook.react.uimanager.events.TouchEventType
import com.facebook.react.views.textinput.ReactEditText

/**
* JSTouchDispatcher handles dispatching touches to JS from RootViews. If you implement RootView you
Expand Down Expand Up @@ -184,6 +187,9 @@ public class JSTouchDispatcher(private val viewGroup: ViewGroup) {
FLog.w(
ReactConstants.TAG, "Warning : touch event was ignored. Action=$action Target=$targetTag")
}

val editTextInZeroContainer = getEditTextInZeroContainer(ev, action)
if (editTextInZeroContainer != null) forwardTouchEventToView(ev, editTextInZeroContainer)
}

private fun markActiveTouchForTag(surfaceId: Int, reactTag: Int, reactContext: ReactContext?) {
Expand All @@ -208,6 +214,28 @@ public class JSTouchDispatcher(private val viewGroup: ViewGroup) {
ev.x, ev.y, viewGroup, targetCoordinates, null)
}

private fun getEditTextInZeroContainer(ev: MotionEvent, action: Int): ReactEditText? {
if (action == MotionEvent.ACTION_DOWN || action == MotionEvent.ACTION_MOVE || action == MotionEvent.ACTION_UP) {
val eventCoords = FloatArray(2)
eventCoords[0] = ev.x
eventCoords[1] = ev.y
val targetView = findTouchTargetViewWithPointerEvents(eventCoords, viewGroup)

if (targetView is ReactEditText && TouchTargetHelper.isInZeroSizedZIndexContainer(targetView, viewGroup)) {
return targetView
}
}
return null
}

private fun forwardTouchEventToView(ev: MotionEvent,targetView: View) {
val newEvent = MotionEvent.obtain(ev)
newEvent.setLocation(targetCoordinates[0], targetCoordinates[1])
targetView.onTouchEvent(newEvent)
newEvent.recycle()
}


private fun dispatchCancelEvent(androidEvent: MotionEvent, eventDispatcher: EventDispatcher) {
// This means the gesture has already ended, via some other CANCEL or UP event. This is not
// expected to happen very often as it would mean some child View has decided to intercept the
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -160,6 +160,21 @@ public object TouchTargetHelper {
return currentView
}

@JvmStatic
public fun isInZeroSizedZIndexContainer(view: View, rootViewGroup: ViewGroup): Boolean {
var parent = view.parent
while (parent != null && parent != rootViewGroup) {
if (parent is ReactZIndexedViewGroup) {
val viewGroup = parent as ViewGroup
if (viewGroup.width <= 0 || viewGroup.height <= 0) {
return true
}
}
parent = parent.parent
}
return false
}

private enum class TouchTargetReturnType {
/** Allow returning the view passed in through the parameters. */
SELF,
Expand Down Expand Up @@ -308,7 +323,7 @@ public object TouchTargetHelper {
* Returns the touch target View of the event given, or null if neither the given View nor any of
* its descendants are the touch target.
*/
private fun findTouchTargetViewWithPointerEvents(
public fun findTouchTargetViewWithPointerEvents(
eventCoords: FloatArray,
view: View,
pathAccumulator: MutableList<ViewTarget>? = null
Expand Down