0
0
mirror of https://github.com/florisboard/florisboard.git synced 2024-09-19 19:42:20 +02:00

Re-implement composers into new editor instance logic

This commit is contained in:
Patrick Goldinger 2022-05-09 23:32:10 +02:00
parent 01de9a4ae1
commit 753fbc30df
5 changed files with 66 additions and 28 deletions

View File

@ -26,6 +26,8 @@ import android.view.KeyEvent
import android.view.inputmethod.InputConnection
import dev.patrickgold.florisboard.FlorisImeService
import dev.patrickgold.florisboard.ime.nlp.BreakIteratorGroup
import dev.patrickgold.florisboard.ime.text.composing.Composer
import dev.patrickgold.florisboard.lib.ext.ExtensionComponentName
import dev.patrickgold.florisboard.lib.kotlin.guardedByLock
import dev.patrickgold.florisboard.subtypeManager
import kotlinx.coroutines.MainScope
@ -40,7 +42,7 @@ abstract class AbstractEditorInstance(context: Context) {
private const val NumCharsBeforeCursor: Int = 256
private const val NumCharsAfterCursor: Int = 128
private const val NumCharsSafeMarginBeforeCursor: Int = 128
private const val NumCharsSafeMarginAfterCursor: Int = 0
//private const val NumCharsSafeMarginAfterCursor: Int = 0
private const val CursorUpdateAll: Int =
InputConnection.CURSOR_UPDATE_MONITOR or InputConnection.CURSOR_UPDATE_IMMEDIATE
@ -235,6 +237,8 @@ abstract class AbstractEditorInstance(context: Context) {
return generateContent(editorInfo, selection, textBeforeSelection, textAfterSelection, selectedText)
}
abstract fun determineComposer(composerName: ExtensionComponentName): Composer
protected open fun shouldDetermineComposingRegion(editorInfo: FlorisEditorInfo): Boolean {
return editorInfo.isRichInputEditor
}
@ -279,6 +283,42 @@ abstract class AbstractEditorInstance(context: Context) {
return true
}
open fun commitChar(char: String): Boolean {
val content = activeContent
val selection = content.selection
// TODO: length enforcement to 1 may be an issue for some Unicode chars which are 2 Java chars
if (char.length != 1 || selection.isNotValid || selection.isSelectionMode || activeInfo.isRawInputEditor) {
return commitText(char)
}
val ic = currentInputConnection() ?: return false
val composer = determineComposer(subtypeManager.activeSubtype().composer)
val previous = content.textBeforeSelection.takeLast(composer.toRead)
val (rm, finalText) = composer.getActions(previous, char[0])
if (rm <= 0) {
commitText(finalText)
} else runBlocking {
ic.beginBatchEdit()
val newSelection = EditorRange.cursor(selection.start - rm + finalText.length)
val newContent = content.generateCopy(
selection = newSelection,
textBeforeSelection = buildString {
append(content.textBeforeSelection.dropLast(rm))
append(finalText)
},
selectedText = "",
)
expectedContentQueue.push(newContent)
// Utilize composing region to replace previous chars without using delete. This avoids flickering in the
// target editor and improves the UX
ic.setComposingRegion(content.selection.start - rm, content.selection.start)
ic.setComposingText(finalText, 1)
// Now set the proper composing region we expect
ic.setComposingRegion(newContent.composing)
ic.endBatchEdit()
}
return true
}
open fun commitText(text: String): Boolean {
val ic = currentInputConnection() ?: return false
val content = activeContent
@ -288,7 +328,7 @@ abstract class AbstractEditorInstance(context: Context) {
if (activeInfo.isRawInputEditor) {
ic.commitText(text, 1)
} else runBlocking {
val newSelection = EditorRange(selection.start + text.length, selection.start + text.length)
val newSelection = EditorRange.cursor(selection.start + text.length)
val newContent = content.generateCopy(
selection = newSelection,
textBeforeSelection = buildString {

View File

@ -34,11 +34,14 @@ import dev.patrickgold.florisboard.ime.clipboard.provider.ItemType
import dev.patrickgold.florisboard.ime.keyboard.KeyboardMode
import dev.patrickgold.florisboard.ime.nlp.TextProcessor
import dev.patrickgold.florisboard.ime.input.InputShiftState
import dev.patrickgold.florisboard.ime.text.composing.Appender
import dev.patrickgold.florisboard.ime.text.composing.Composer
import dev.patrickgold.florisboard.ime.text.key.KeyVariation
import dev.patrickgold.florisboard.keyboardManager
import dev.patrickgold.florisboard.lib.android.AndroidVersion
import dev.patrickgold.florisboard.lib.android.showShortToast
import dev.patrickgold.florisboard.lib.devtools.flogDebug
import dev.patrickgold.florisboard.lib.ext.ExtensionComponentName
import kotlinx.coroutines.MainScope
import kotlinx.coroutines.launch
import kotlinx.coroutines.runBlocking
@ -141,6 +144,10 @@ class EditorInstance(context: Context) : AbstractEditorInstance(context) {
super.handleFinishInputView()
}
override fun determineComposer(composerName: ExtensionComponentName): Composer {
return keyboardManager.resources.composers.value?.get(composerName) ?: Appender.DefaultInstance
}
override fun shouldDetermineComposingRegion(editorInfo: FlorisEditorInfo): Boolean {
return super.shouldDetermineComposingRegion(editorInfo) &&
(phantomSpace.isInactive || phantomSpace.showComposingRegion)
@ -161,6 +168,16 @@ class EditorInstance(context: Context) : AbstractEditorInstance(context) {
return super.setSelection(selection)
}
override fun commitChar(char: String): Boolean {
val isPhantomSpaceActive = phantomSpace.determine(char)
phantomSpace.setInactive()
return if (isPhantomSpaceActive) {
super.commitChar("$SPACE$char")
} else {
super.commitChar(char)
}
}
/**
* Commits the given [text] to this editor instance and adjusts both the cursor position and
* composing region, if any.
@ -220,31 +237,6 @@ class EditorInstance(context: Context) : AbstractEditorInstance(context) {
}
}
/**
* Internal helper, replacing a call to currentInputConnection().commitText with text composition in mind.
*/
//private fun doCommitText(text: String): Pair<String, Boolean> {
// val ic = currentInputConnection() ?: return "" to false
// val composer = keyboardManager.resources.composers.value?.get(subtypeManager.activeSubtype().composer) ?: Appender()
// return if (text.length != 1) {
// ic.commitText(text, 1)
// } else {
// ic.beginBatchEdit()
// ic.finishComposingText()
// val previous = getTextBeforeCursor(composer.toRead)
// val (rm, finalText) = composer.getActions(previous, text[0])
// if (rm == 0) {
// ic.commitText(finalText, 1)
// } else {
// val et = ic.getExtractedText(ExtractedTextRequest(), 0)
// ic.setComposingRegion(et.selectionStart-rm, et.selectionStart)
// ic.setComposingText(finalText, 1)
// }
// ic.endBatchEdit()
// Pair(true, finalText)
// }
//}
/**
* Commits the given [ClipboardItem]. If the clip data is text (incl. HTML), it delegates to [commitText].
* If the item has a content URI (and the EditText supports it), the item is committed as rich data.

View File

@ -56,6 +56,8 @@ data class EditorRange(val start: Int, val end: Int) {
/** Unspecified range */
val Unspecified = EditorRange(-1, -1)
fun cursor(position: Int) = EditorRange(start = position, end = position)
fun normalized(start: Int, end: Int) = EditorRange(start = min(start, end), end = max(start, end))
}
}

View File

@ -710,7 +710,7 @@ class KeyboardManager(context: Context) : InputKeyEventReceiver {
else -> when (data.type) {
KeyType.CHARACTER, KeyType.NUMERIC ->{
val text = data.asString(isForDisplay = false)
editorInstance.commitText(text)
editorInstance.commitChar(text)
}
else -> {
flogError(LogTopic.KEY_EVENTS) { "Received unknown key: $data" }

View File

@ -17,6 +17,10 @@ interface Composer {
@Serializable
@SerialName("appender")
class Appender : Composer {
companion object {
val DefaultInstance = Appender()
}
override val id: String = "appender"
override val label: String = "Appender"
override val toRead: Int = 0