Skip to content

Commit

Permalink
Support singleLine and KeyboardAction on iOS
Browse files Browse the repository at this point in the history
- Added UIKitTextInputService.runImeActionIfRequired that calls the
  current keyboard action if needed
- Added UIKitTextInputService.onKeyEvent that calls a keyboard
  action when required
- Prevented UIKitTextInputService.skikoInput from adding a new line
  character when singleLine = true or a keyboard action needs to be
  called
- Passed UIKitTextInputService::onKeyEvent to ComposeLayer.setContent
  from ComposeWindow
  • Loading branch information
paxbun committed Jul 22, 2023
1 parent 41556ad commit 9d2905b
Show file tree
Hide file tree
Showing 2 changed files with 52 additions and 14 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -16,9 +16,12 @@

package androidx.compose.ui.platform

import androidx.compose.ui.input.key.KeyEvent
import androidx.compose.ui.text.input.*
import kotlin.math.min
import org.jetbrains.skiko.SkikoInput
import org.jetbrains.skiko.SkikoKey
import org.jetbrains.skiko.SkikoKeyboardEventKind
import org.jetbrains.skiko.ios.SkikoUITextInputTraits

import platform.UIKit.*
Expand All @@ -42,6 +45,7 @@ internal class UIKitTextInputService(
private val _hideSoftwareKeyboard: () -> Unit = hideSoftwareKeyboard
private var currentInput: CurrentInput? = null
private var currentImeOptions: ImeOptions? = null
private var currentImeActionHandler: ((ImeAction) -> Unit)? = null

/**
* Workaround to fix voice dictation.
Expand All @@ -61,12 +65,14 @@ internal class UIKitTextInputService(
) {
currentInput = CurrentInput(value, onEditCommand)
currentImeOptions = imeOptions
currentImeActionHandler = onImeActionPerformed
showSoftwareKeyboard()
}

override fun stopInput() {
currentInput = null
currentImeOptions = null
currentImeActionHandler = null
hideSoftwareKeyboard()
}

Expand All @@ -80,7 +86,8 @@ internal class UIKitTextInputService(

override fun updateState(oldValue: TextFieldValue?, newValue: TextFieldValue) {
val textChanged = oldValue == null || oldValue.text != newValue.text
val selectionChanged = textChanged || oldValue == null || oldValue.selection != newValue.selection
val selectionChanged =
textChanged || oldValue == null || oldValue.selection != newValue.selection
if (textChanged) {
textWillChange()
}
Expand Down Expand Up @@ -115,6 +122,11 @@ internal class UIKitTextInputService(
* @param text A string object representing the character typed on the system keyboard.
*/
override fun insertText(text: String) {
if (text == "\n") {
if (runImeActionIfRequired()) {
return
}
}
getCursorPos()?.let {
_tempCursorPos = it + text.length
}
Expand Down Expand Up @@ -318,6 +330,17 @@ internal class UIKitTextInputService(

}

fun onKeyEvent(event: KeyEvent): Boolean {
val nativeKeyEvent = event.nativeKeyEvent
if (nativeKeyEvent.kind != SkikoKeyboardEventKind.UP) {
return false
}
if (nativeKeyEvent.key != SkikoKey.KEY_ENTER) {
return false
}
return runImeActionIfRequired()
}

private fun sendEditCommand(vararg commands: EditCommand) {
currentInput?.let { input ->
input.onEditCommand(commands.toList())
Expand All @@ -335,6 +358,19 @@ internal class UIKitTextInputService(
return null
}

private fun imeActionRequired(): Boolean =
currentImeOptions?.run { singleLine || imeAction != ImeAction.None } ?: false

private fun runImeActionIfRequired(): Boolean {
val imeAction = currentImeOptions?.imeAction ?: return false
val imeActionHandler = currentImeActionHandler ?: return false
if (!imeActionRequired()) {
return false
}
imeActionHandler(imeAction)
return true
}

private fun getState(): TextFieldValue? = currentInput?.value

}
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,6 @@ import androidx.compose.runtime.CompositionLocalProvider
import androidx.compose.runtime.InternalComposeApi
import androidx.compose.runtime.mutableStateOf
import androidx.compose.ui.createSkiaLayer
import androidx.compose.ui.geometry.Offset
import androidx.compose.ui.geometry.Rect
import androidx.compose.ui.input.InputMode
import androidx.compose.ui.interop.LocalLayerContainer
Expand Down Expand Up @@ -328,18 +327,21 @@ internal actual class ComposeWindow : UIViewController {
platform = uiKitPlatform,
input = uiKitTextInputService.skikoInput,
)
layer.setContent(content = {
CompositionLocalProvider(
LocalLayerContainer provides rootView,
LocalUIViewController provides this,
LocalKeyboardOverlapHeightState provides keyboardOverlapHeightState,
LocalSafeAreaState provides safeAreaState,
LocalLayoutMarginsState provides layoutMarginsState,
LocalInterfaceOrientationState provides interfaceOrientationState,
) {
content()
}
})
layer.setContent(
onKeyEvent = uiKitTextInputService::onKeyEvent,
content = {
CompositionLocalProvider(
LocalLayerContainer provides rootView,
LocalUIViewController provides this,
LocalKeyboardOverlapHeightState provides keyboardOverlapHeightState,
LocalSafeAreaState provides safeAreaState,
LocalLayoutMarginsState provides layoutMarginsState,
LocalInterfaceOrientationState provides interfaceOrientationState,
) {
content()
}
},
)
}

override fun traitCollectionDidChange(previousTraitCollection: UITraitCollection?) {
Expand Down

0 comments on commit 9d2905b

Please sign in to comment.