diff --git a/compose/foundation/foundation/src/skikoMain/kotlin/androidx/compose/foundation/text/CupertinoTextFieldPointerModifier.skiko.kt b/compose/foundation/foundation/src/skikoMain/kotlin/androidx/compose/foundation/text/CupertinoTextFieldPointerModifier.skiko.kt index 90ef1744f6347..c33c2436ad315 100644 --- a/compose/foundation/foundation/src/skikoMain/kotlin/androidx/compose/foundation/text/CupertinoTextFieldPointerModifier.skiko.kt +++ b/compose/foundation/foundation/src/skikoMain/kotlin/androidx/compose/foundation/text/CupertinoTextFieldPointerModifier.skiko.kt @@ -24,6 +24,7 @@ import androidx.compose.foundation.text.selection.TextFieldSelectionManager import androidx.compose.foundation.text.selection.awaitSelectionGestures import androidx.compose.foundation.text.selection.getTextFieldSelectionLayout import androidx.compose.foundation.text.selection.isSelectionHandleInVisibleBound +import androidx.compose.foundation.text.selection.updateSelectionTouchMode import androidx.compose.runtime.Composable import androidx.compose.runtime.getValue import androidx.compose.runtime.rememberUpdatedState @@ -50,31 +51,28 @@ internal fun Modifier.cupertinoTextFieldPointer( readOnly: Boolean, offsetMapping: OffsetMapping ): Modifier = if (enabled) { - // TODO switch to ".updateSelectionTouchMode { state.isInTouchMode = it }" as in defaultTextFieldPointer - if (isInTouchMode) { - val longPressHandlerModifier = getLongPressHandlerModifier(state, offsetMapping, manager) - val tapHandlerModifier = getTapHandlerModifier( - interactionSource, - state, - focusRequester, - readOnly, - offsetMapping, - manager + this + .updateSelectionTouchMode { + state.isInTouchMode = it + } + .then( + getTapHandlerModifier( + interactionSource, + state, + focusRequester, + readOnly, + offsetMapping, + manager + ) ) - this - .then(tapHandlerModifier) - .then(longPressHandlerModifier) - .pointerHoverIcon(PointerIcon.Text) - } else { - this - .pointerInput(manager.mouseSelectionObserver, manager.touchSelectionObserver) { - awaitSelectionGestures( - manager.mouseSelectionObserver, - manager.touchSelectionObserver, - ) - } - .pointerHoverIcon(PointerIcon.Text) - } + .then(getLongPressHandlerModifier(state, offsetMapping, manager)) + .pointerInput(manager.mouseSelectionObserver, manager.touchSelectionObserver) { + awaitSelectionGestures( + manager.mouseSelectionObserver, + manager.touchSelectionObserver, + ) + } + .pointerHoverIcon(PointerIcon.Text) } else { this } diff --git a/compose/foundation/foundation/src/skikoMain/kotlin/androidx/compose/foundation/text/input/internal/LegacyPlatformTextInputServiceAdapter.skiko.kt b/compose/foundation/foundation/src/skikoMain/kotlin/androidx/compose/foundation/text/input/internal/LegacyPlatformTextInputServiceAdapter.skiko.kt index 3269e42bf5362..cf948c82b2684 100644 --- a/compose/foundation/foundation/src/skikoMain/kotlin/androidx/compose/foundation/text/input/internal/LegacyPlatformTextInputServiceAdapter.skiko.kt +++ b/compose/foundation/foundation/src/skikoMain/kotlin/androidx/compose/foundation/text/input/internal/LegacyPlatformTextInputServiceAdapter.skiko.kt @@ -164,7 +164,10 @@ internal actual fun createLegacyPlatformTextInputServiceAdapter(): focusedRectInRoot = { focusedRectInRoot }, textFieldRectInRoot = { textFieldRectInRoot }, textClippingRectInRoot = { textClippingRectInRoot }, - editText = editBlock + editText = editBlock, + updateTouchMode = { + textInputModifierNode?.legacyTextFieldState?.isInTouchMode = it + } ) } } diff --git a/compose/foundation/foundation/src/skikoMain/kotlin/androidx/compose/foundation/text/input/internal/TextInputSession.skiko.kt b/compose/foundation/foundation/src/skikoMain/kotlin/androidx/compose/foundation/text/input/internal/TextInputSession.skiko.kt index f5b7d3807a2ca..a5578ecb823e2 100644 --- a/compose/foundation/foundation/src/skikoMain/kotlin/androidx/compose/foundation/text/input/internal/TextInputSession.skiko.kt +++ b/compose/foundation/foundation/src/skikoMain/kotlin/androidx/compose/foundation/text/input/internal/TextInputSession.skiko.kt @@ -111,7 +111,8 @@ internal actual suspend fun PlatformTextInputSession.platformSpecificTextInputSe focusedRectInRoot = ::focusedRectInRoot, textFieldRectInRoot = ::textFieldRectInRoot, textClippingRectInRoot = ::textClippingRectInRoot, - editText = ::editText + editText = ::editText, + updateTouchMode = { updateTouchMode(it) } ) ) } @@ -235,5 +236,6 @@ internal data class SkikoPlatformTextInputMethodRequest( override val focusedRectInRoot: () -> Rect?, override val textFieldRectInRoot: () -> Rect?, override val textClippingRectInRoot: () -> Rect?, - override val editText: (block: TextEditingScope.() -> Unit) -> Unit + override val editText: (block: TextEditingScope.() -> Unit) -> Unit, + override val updateTouchMode: (Boolean) -> Unit, ): PlatformTextInputMethodRequest diff --git a/compose/ui/ui/src/skikoMain/kotlin/androidx/compose/ui/platform/PlatformTextInputMethodRequest.skiko.kt b/compose/ui/ui/src/skikoMain/kotlin/androidx/compose/ui/platform/PlatformTextInputMethodRequest.skiko.kt index 43f839877596d..cacdd278d5c6b 100644 --- a/compose/ui/ui/src/skikoMain/kotlin/androidx/compose/ui/platform/PlatformTextInputMethodRequest.skiko.kt +++ b/compose/ui/ui/src/skikoMain/kotlin/androidx/compose/ui/platform/PlatformTextInputMethodRequest.skiko.kt @@ -87,4 +87,7 @@ actual interface PlatformTextInputMethodRequest { */ @ExperimentalComposeUiApi val editText: (block: TextEditingScope.() -> Unit) -> Unit + + @ExperimentalComposeUiApi + val updateTouchMode: (Boolean) -> Unit } diff --git a/compose/ui/ui/src/uikitMain/kotlin/androidx/compose/ui/platform/UIKitTextInputService.uikit.kt b/compose/ui/ui/src/uikitMain/kotlin/androidx/compose/ui/platform/UIKitTextInputService.uikit.kt index a46818d8a2ecf..1f1d6b29d1352 100644 --- a/compose/ui/ui/src/uikitMain/kotlin/androidx/compose/ui/platform/UIKitTextInputService.uikit.kt +++ b/compose/ui/ui/src/uikitMain/kotlin/androidx/compose/ui/platform/UIKitTextInputService.uikit.kt @@ -134,6 +134,10 @@ internal class UIKitTextInputService( private var _tempCursorPos: Int? = null private val mainScope = MainScope() + fun setUpdateTouchMode(updateTouchMode: ((Boolean) -> Unit)?) { + textUIView?.updateTouchMode = updateTouchMode + } + override fun startInput( value: TextFieldValue, imeOptions: ImeOptions, diff --git a/compose/ui/ui/src/uikitMain/kotlin/androidx/compose/ui/scene/ComposeSceneMediator.uikit.kt b/compose/ui/ui/src/uikitMain/kotlin/androidx/compose/ui/scene/ComposeSceneMediator.uikit.kt index 141859536fddc..bf1180de0a620 100644 --- a/compose/ui/ui/src/uikitMain/kotlin/androidx/compose/ui/scene/ComposeSceneMediator.uikit.kt +++ b/compose/ui/ui/src/uikitMain/kotlin/androidx/compose/ui/scene/ComposeSceneMediator.uikit.kt @@ -744,6 +744,8 @@ internal class ComposeSceneMediator( onImeActionPerformed = request.onImeAction ?: {} ) + textInputService.setUpdateTouchMode(request.updateTouchMode) + continuation.invokeOnCancellation { textInputService.stopInput() } diff --git a/compose/ui/ui/src/uikitMain/kotlin/androidx/compose/ui/window/IntermediateTextInputUIView.uikit.kt b/compose/ui/ui/src/uikitMain/kotlin/androidx/compose/ui/window/IntermediateTextInputUIView.uikit.kt index b13ff59a9e30d..bb7e3bff8eb74 100644 --- a/compose/ui/ui/src/uikitMain/kotlin/androidx/compose/ui/window/IntermediateTextInputUIView.uikit.kt +++ b/compose/ui/ui/src/uikitMain/kotlin/androidx/compose/ui/window/IntermediateTextInputUIView.uikit.kt @@ -49,6 +49,14 @@ import platform.UIKit.NSWritingDirectionNatural import platform.UIKit.UIEvent import platform.UIKit.UIKeyInputProtocol import platform.UIKit.UIKeyboardAppearance +import platform.UIKit.UIKeyboardHIDUsageKeyboardLeftAlt +import platform.UIKit.UIKeyboardHIDUsageKeyboardLeftControl +import platform.UIKit.UIKeyboardHIDUsageKeyboardLeftGUI +import platform.UIKit.UIKeyboardHIDUsageKeyboardLeftShift +import platform.UIKit.UIKeyboardHIDUsageKeyboardRightAlt +import platform.UIKit.UIKeyboardHIDUsageKeyboardRightControl +import platform.UIKit.UIKeyboardHIDUsageKeyboardRightGUI +import platform.UIKit.UIKeyboardHIDUsageKeyboardRightShift import platform.UIKit.UIKeyboardType import platform.UIKit.UIPress import platform.UIKit.UIPressesEvent @@ -95,11 +103,13 @@ internal class IntermediateTextInputUIView( private val mainScope = MainScope() /** - * Callback to handle keyboard presses. The parameter is a [Set] of [UIPress] objects. + * Callback to handle physical keyboard presses. The parameter is a [Set] of [UIPress] objects. * Erasure happens due to K/N not supporting Obj-C lightweight generics. */ var onKeyboardPresses: (Set<*>) -> Unit = NoOpOnKeyboardPresses + var updateTouchMode: ((Boolean) -> Unit)? = null + var inputTraits: SkikoUITextInputTraits = EmptyInputTraits override fun inputView(): UIView? = inputTraits.inputView() @@ -126,8 +136,13 @@ internal class IntermediateTextInputUIView( } override fun pressesBegan(presses: Set<*>, withEvent: UIPressesEvent?) { - onKeyboardPresses(presses) + @Suppress("UNCHECKED_CAST") + if (!onlyModifiersPressed(presses as Set)) { + // Touch mode update, this shouldn't be triggered if only modifiers keys are pressed + updateTouchMode?.invoke(false) + } + onKeyboardPresses(presses) super.pressesBegan(presses, withEvent) } @@ -136,6 +151,10 @@ internal class IntermediateTextInputUIView( super.pressesEnded(presses, withEvent) } + private fun onlyModifiersPressed(presses: Set): Boolean { + return presses.all { it.key?.keyCode in modifierKeys } + } + override fun hitTest(point: CValue, withEvent: UIEvent?): UIView? { return if (input == null) { null @@ -580,3 +599,17 @@ private fun UITextRange.toTextRange(): TextRange { private fun TextRange.toUITextRange(): UITextRange = IntermediateTextRange(start = start, end = end) + +/** + * A set of keyboard modifier keys used for handling input events. + */ +private val modifierKeys = setOf( + UIKeyboardHIDUsageKeyboardLeftShift, + UIKeyboardHIDUsageKeyboardRightShift, + UIKeyboardHIDUsageKeyboardLeftControl, + UIKeyboardHIDUsageKeyboardRightControl, + UIKeyboardHIDUsageKeyboardLeftAlt, + UIKeyboardHIDUsageKeyboardRightAlt, + UIKeyboardHIDUsageKeyboardLeftGUI, // Left Command / Super / Win + UIKeyboardHIDUsageKeyboardRightGUI, // Right Command / Super / Win +) \ No newline at end of file