diff --git a/core-ui/src/main/java/com/anytypeio/anytype/core_ui/common/Markup.kt b/core-ui/src/main/java/com/anytypeio/anytype/core_ui/common/Markup.kt index ae92cd113f..3c94333d38 100644 --- a/core-ui/src/main/java/com/anytypeio/anytype/core_ui/common/Markup.kt +++ b/core-ui/src/main/java/com/anytypeio/anytype/core_ui/common/Markup.kt @@ -16,6 +16,7 @@ import com.anytypeio.anytype.core_utils.ext.VALUE_ROUNDED import com.anytypeio.anytype.core_utils.ext.removeSpans import com.anytypeio.anytype.presentation.editor.editor.Markup import com.anytypeio.anytype.core_models.ThemeColor +import com.anytypeio.anytype.core_ui.extensions.disable import timber.log.Timber fun Markup.toSpannable( @@ -498,8 +499,7 @@ fun Editable.proceedWithSettingMentionSpan( fun Editable.setClickableSpan(click: ((String) -> Unit)?, mark: Markup.Mark.Mention) { val clickableSpan = object : ClickableSpan() { override fun onClick(widget: View) { - // TODO consider pausing text watchers. Otherwise, redundant text watcher events will be triggered. - (widget as? TextInputWidget)?.enableReadMode() + (widget as? TextInputWidget)?.disable() click?.invoke(mark.param) } } diff --git a/core-ui/src/main/java/com/anytypeio/anytype/core_ui/extensions/TextInputWidgetExtension.kt b/core-ui/src/main/java/com/anytypeio/anytype/core_ui/extensions/TextInputWidgetExtension.kt index dd01693a5f..a144b0af94 100644 --- a/core-ui/src/main/java/com/anytypeio/anytype/core_ui/extensions/TextInputWidgetExtension.kt +++ b/core-ui/src/main/java/com/anytypeio/anytype/core_ui/extensions/TextInputWidgetExtension.kt @@ -1,16 +1,9 @@ package com.anytypeio.anytype.core_ui.extensions -import android.graphics.Point import com.anytypeio.anytype.core_ui.common.isLinksOrMentionsPresent import com.anytypeio.anytype.core_ui.widgets.text.TextInputWidget import com.anytypeio.anytype.presentation.editor.editor.Markup -fun TextInputWidget.preserveSelection(block: () -> Unit) = synchronized(this) { - val selection = selectionStart..selectionEnd - block() - setSelection(selection.first, selection.last) -} - fun TextInputWidget.applyMovementMethod(item: Markup) { if (item.marks.isNotEmpty() && item.marks.isLinksOrMentionsPresent()) { setLinksClickable() @@ -19,12 +12,9 @@ fun TextInputWidget.applyMovementMethod(item: Markup) { } } -fun TextInputWidget.getSelectionCoords(): Point { - val pos = selectionStart - val line = layout.getLineForOffset(pos) - val baseline = layout.getLineBaseline(line) - val ascent = layout.getLineAscent(line) - val x = layout.getPrimaryHorizontal(pos).toInt() - val y = baseline + ascent - return Point(x, y) +fun TextInputWidget.disable() { + isEnabled = false + pauseTextWatchers { + enableReadMode() + } } \ No newline at end of file diff --git a/core-ui/src/main/java/com/anytypeio/anytype/core_ui/features/editor/EditorTouchProcessor.kt b/core-ui/src/main/java/com/anytypeio/anytype/core_ui/features/editor/EditorTouchProcessor.kt index 18b3d0aa4e..4a41ed8e6f 100644 --- a/core-ui/src/main/java/com/anytypeio/anytype/core_ui/features/editor/EditorTouchProcessor.kt +++ b/core-ui/src/main/java/com/anytypeio/anytype/core_ui/features/editor/EditorTouchProcessor.kt @@ -4,10 +4,13 @@ import android.os.Build import android.os.Handler import android.os.Looper import android.os.SystemClock +import android.text.Layout +import android.text.style.ClickableSpan import android.view.HapticFeedbackConstants import android.view.MotionEvent import android.view.View import android.view.ViewConfiguration +import com.anytypeio.anytype.core_ui.extensions.disable import com.anytypeio.anytype.core_ui.widgets.text.TextInputWidget import timber.log.Timber import kotlin.math.abs @@ -84,6 +87,33 @@ class EditorTouchProcessor( moves.clear() Timber.d("ACTION UP") actionHandler.removeCallbacksAndMessages(null) + + /** + * When clicking on mention text, the code contains two separate logics + * that handle clicks on this text: the EditorTouchProcessor, + * which is responsible for triggering drag-and-drop or long-click mode, + * and the ClickableSpan, which is applied to the mention text. + * Since it is impossible to predict which listener will execute first, + * the click on the mention is handled in two different places. + * @see fun Editable.setClickableSpan(click: ((String) -> Unit)?, mark: Markup.Mark.Mention) + */ + if (v is TextInputWidget) { + val x = (event.x - v.totalPaddingLeft + v.scrollX).toInt() + val y = (event.y - v.totalPaddingTop + v.scrollY).toInt() + + val layout: Layout = v.layout + val line = layout.getLineForVertical(y) + val offset = layout.getOffsetForHorizontal(line, x.toFloat()) + + val link = + v.editableText.getSpans(offset, offset, ClickableSpan::class.java) + if (link.isNotEmpty()) { + v.disable() + link[0].onClick(v) + return true + } + } + return when (actionUpStartInMillis.untilNow()) { in LONG_PRESS_TIMEOUT..DND_TIMEOUT -> { onLongClick()