Skip to content
Merged
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 @@ -30,8 +30,7 @@ class LinkLongPressMovementMethod : ArrowKeyMovementMethod() {
private var pressedLink: LinkSpan? = null
private var pressedSpoiler: SpoilerSpan? = null

var isLinkTouchActive: Boolean = false
private set
private var isLinkTouchActive: Boolean = false
private var isTouchWithinTextBounds: Boolean = true

override fun onTouchEvent(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,6 @@ import com.swmansion.enriched.markdown.utils.text.view.applySelectableState
import com.swmansion.enriched.markdown.utils.text.view.cancelJSTouchForCheckboxTap
import com.swmansion.enriched.markdown.utils.text.view.cancelJSTouchForLinkTap
import com.swmansion.enriched.markdown.utils.text.view.createSelectionActionModeCallback
import com.swmansion.enriched.markdown.utils.text.view.reallowParentInterceptIfLinkReleased
import com.swmansion.enriched.markdown.utils.text.view.setupAsMarkdownTextView

class EnrichedMarkdownInternalText
Expand Down Expand Up @@ -116,10 +115,7 @@ class EnrichedMarkdownInternalText
return true
}
val result = super.onTouchEvent(event)
when (event.action) {
MotionEvent.ACTION_DOWN -> cancelJSTouchForLinkTap(event)
else -> reallowParentInterceptIfLinkReleased()
}
if (event.action == MotionEvent.ACTION_DOWN) cancelJSTouchForLinkTap(event)
return result
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -47,7 +47,6 @@ import com.swmansion.enriched.markdown.utils.text.view.createSelectionActionMode
import com.swmansion.enriched.markdown.utils.text.view.emitImagePressEvent
import com.swmansion.enriched.markdown.utils.text.view.emitLinkLongPressEvent
import com.swmansion.enriched.markdown.utils.text.view.emitLinkPressEvent
import com.swmansion.enriched.markdown.utils.text.view.reallowParentInterceptIfLinkReleased
import com.swmansion.enriched.markdown.utils.text.view.setupAsMarkdownTextView
import java.util.concurrent.Executors
import kotlin.math.ceil
Expand Down Expand Up @@ -620,10 +619,7 @@ class EnrichedMarkdownText
return true
}
val result = super.onTouchEvent(event)
when (event.action) {
MotionEvent.ACTION_DOWN -> cancelJSTouchForLinkTap(event)
else -> reallowParentInterceptIfLinkReleased()
}
if (event.action == MotionEvent.ACTION_DOWN) cancelJSTouchForLinkTap(event)
return result
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,6 @@ import com.swmansion.enriched.markdown.utils.text.extensions.replaceMathSpansWit
import com.swmansion.enriched.markdown.utils.text.span.prepareWidthAwareSpans
import com.swmansion.enriched.markdown.utils.text.view.LinkLongPressMovementMethod
import com.swmansion.enriched.markdown.utils.text.view.cancelJSTouchForLinkTap
import com.swmansion.enriched.markdown.utils.text.view.reallowParentInterceptIfLinkReleased
import com.swmansion.enriched.markdown.views.ContextMenuPopup
import kotlin.math.ceil
import kotlin.math.max
Expand Down Expand Up @@ -621,10 +620,7 @@ class TableContainerView(

override fun onTouchEvent(event: MotionEvent): Boolean {
val result = super.onTouchEvent(event)
when (event.action) {
MotionEvent.ACTION_DOWN -> cancelJSTouchForLinkTap(event)
else -> reallowParentInterceptIfLinkReleased()
}
if (event.action == MotionEvent.ACTION_DOWN) cancelJSTouchForLinkTap(event)
return result
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -38,34 +38,16 @@ fun View.emitLinkLongPressEvent(url: String) {
* Cancels the JS touch for an active link tap, preventing parent
* Pressable/TouchableOpacity from firing onPress for the same tap.
*
* Also requests that parent ViewGroups do not intercept subsequent touch
* events in this gesture. This is necessary for react-native-gesture-handler's
* Pressable, which intercepts at the native ViewGroup level and would otherwise
* steal the gesture before ACTION_UP reaches the TextView.
* Parent intercept is handled by [LinkLongPressMovementMethod], which
* disallows it for the press and releases it when the press ends.
*/
fun TextView.cancelJSTouchForLinkTap(event: MotionEvent) {
val currentMovementMethod = movementMethod
if (currentMovementMethod is LinkLongPressMovementMethod && currentMovementMethod.isLinkTouchActive) {
parent?.requestDisallowInterceptTouchEvent(true)
NativeGestureUtil.notifyNativeGestureStarted(this, event)
}
}

/**
* Re-allows parent ViewGroups to intercept touch events once the link
* touch is no longer active (slop exceeded, gesture ended, or cancelled).
* This restores normal scrolling behavior for parent ScrollView/RecyclerView.
*
* Must be called after [LinkLongPressMovementMethod.onTouchEvent] has
* processed the event, since that is where [isLinkTouchActive] is updated.
*/
fun TextView.reallowParentInterceptIfLinkReleased() {
val currentMovementMethod = movementMethod
if (currentMovementMethod is LinkLongPressMovementMethod && !currentMovementMethod.isLinkTouchActive) {
parent?.requestDisallowInterceptTouchEvent(false)
}
}

/**
* Cancels the JS touch unconditionally, preventing parent
* Pressable/TouchableOpacity from firing onPress for the same gesture.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,7 @@ class LinkLongPressMovementMethod : ArrowKeyMovementMethod() {
var isLinkTouchActive: Boolean = false
private set
private var isTouchWithinTextBounds: Boolean = true
private var disallowsParentIntercept = false

override fun onTouchEvent(
widget: TextView,
Expand Down Expand Up @@ -71,6 +72,9 @@ class LinkLongPressMovementMethod : ArrowKeyMovementMethod() {
isLinkTouchActive = pressedLink != null || pressedImage != null || pressedCodeBlock != null
isTouchWithinTextBounds = charOffsetAt(widget, event) != null || pressedCodeBlock != null
pressedLink?.let { scheduleLongPress(widget, it) }
// Keeps RNGH Pressable and scrolling parents from stealing the press before ACTION_UP.
disallowsParentIntercept = isLinkTouchActive
if (isLinkTouchActive) widget.parent?.requestDisallowInterceptTouchEvent(true)
}

MotionEvent.ACTION_MOVE -> {
Expand All @@ -83,6 +87,7 @@ class LinkLongPressMovementMethod : ArrowKeyMovementMethod() {
pressedLink = null
pressedImage = null
pressedCodeBlock = null
releaseParentIntercept(widget)
}
}

Expand All @@ -95,6 +100,7 @@ class LinkLongPressMovementMethod : ArrowKeyMovementMethod() {
pressedLink = null
pressedImage = null
pressedCodeBlock = null
releaseParentIntercept(widget)

if (handleSpoilerTap(widget, buffer, event)) {
return true
Expand Down Expand Up @@ -125,6 +131,7 @@ class LinkLongPressMovementMethod : ArrowKeyMovementMethod() {
pressedLink = null
pressedImage = null
pressedCodeBlock = null
releaseParentIntercept(widget)
}
}

Expand Down Expand Up @@ -163,6 +170,14 @@ class LinkLongPressMovementMethod : ArrowKeyMovementMethod() {
longPressRunnable = null
}

// Undo only our own request: a long-press drag selection relies on the Editor's
// request staying in place until ACTION_UP, or a scrolling parent steals the drag.
private fun releaseParentIntercept(widget: TextView) {
if (!disallowsParentIntercept) return
disallowsParentIntercept = false
widget.parent?.requestDisallowInterceptTouchEvent(false)
}

private fun charOffsetAt(
widget: TextView,
event: MotionEvent,
Expand Down
Loading