mirror of
https://github.com/Helium314/HeliBoard.git
synced 2025-05-18 16:03:12 +00:00
move cursor in whole codepoints on space / delete swipe
and when space-swiping across emoji, send fake arrow keypress instead (discouraged, but avoids e.g. splitting pirate flag emoji and thus justified) fixes #859
This commit is contained in:
parent
5b1f40f0f6
commit
d91350524a
3 changed files with 47 additions and 13 deletions
|
@ -6,6 +6,9 @@ import helium314.keyboard.latin.LatinIME
|
||||||
import helium314.keyboard.latin.RichInputMethodManager
|
import helium314.keyboard.latin.RichInputMethodManager
|
||||||
import helium314.keyboard.latin.common.Constants
|
import helium314.keyboard.latin.common.Constants
|
||||||
import helium314.keyboard.latin.common.InputPointers
|
import helium314.keyboard.latin.common.InputPointers
|
||||||
|
import helium314.keyboard.latin.common.StringUtils
|
||||||
|
import helium314.keyboard.latin.common.loopOverCodePoints
|
||||||
|
import helium314.keyboard.latin.common.loopOverCodePointsBackwards
|
||||||
import helium314.keyboard.latin.inputlogic.InputLogic
|
import helium314.keyboard.latin.inputlogic.InputLogic
|
||||||
import helium314.keyboard.latin.settings.Settings
|
import helium314.keyboard.latin.settings.Settings
|
||||||
import kotlin.math.abs
|
import kotlin.math.abs
|
||||||
|
@ -80,7 +83,23 @@ class KeyboardActionListenerImpl(private val latinIME: LatinIME, private val inp
|
||||||
override fun onMoveDeletePointer(steps: Int) {
|
override fun onMoveDeletePointer(steps: Int) {
|
||||||
inputLogic.finishInput()
|
inputLogic.finishInput()
|
||||||
val end = inputLogic.mConnection.expectedSelectionEnd
|
val end = inputLogic.mConnection.expectedSelectionEnd
|
||||||
val start = inputLogic.mConnection.expectedSelectionStart + steps
|
var actualSteps = 0 // corrected steps to avoid splitting chars belonging to the same codepoint
|
||||||
|
if (steps > 0) {
|
||||||
|
val text = inputLogic.mConnection.getSelectedText(0)
|
||||||
|
if (text == null) actualSteps = steps
|
||||||
|
else loopOverCodePoints(text) {
|
||||||
|
actualSteps += Character.charCount(it)
|
||||||
|
actualSteps >= steps
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
val text = inputLogic.mConnection.getTextBeforeCursor(-steps * 4, 0)
|
||||||
|
if (text == null) actualSteps = steps
|
||||||
|
else loopOverCodePointsBackwards(text) {
|
||||||
|
actualSteps -= Character.charCount(it)
|
||||||
|
actualSteps <= steps
|
||||||
|
}
|
||||||
|
}
|
||||||
|
val start = inputLogic.mConnection.expectedSelectionStart + actualSteps
|
||||||
if (start > end) return
|
if (start > end) return
|
||||||
inputLogic.mConnection.setSelection(start, end)
|
inputLogic.mConnection.setSelection(start, end)
|
||||||
}
|
}
|
||||||
|
@ -120,29 +139,44 @@ class KeyboardActionListenerImpl(private val latinIME: LatinIME, private val inp
|
||||||
|
|
||||||
private fun onMoveCursorHorizontally(rawSteps: Int): Boolean {
|
private fun onMoveCursorHorizontally(rawSteps: Int): Boolean {
|
||||||
if (rawSteps == 0) return false
|
if (rawSteps == 0) return false
|
||||||
var steps = rawSteps
|
|
||||||
// for RTL languages we want to invert pointer movement
|
// for RTL languages we want to invert pointer movement
|
||||||
if (RichInputMethodManager.getInstance().currentSubtype.isRtlSubtype) steps = -steps
|
val steps = if (RichInputMethodManager.getInstance().currentSubtype.isRtlSubtype) -rawSteps else rawSteps
|
||||||
val moveSteps: Int
|
val moveSteps: Int
|
||||||
if (steps < 0) {
|
if (steps < 0) {
|
||||||
val availableCharacters = inputLogic.mConnection.getTextBeforeCursor(64, 0)?.length ?: return false
|
var actualSteps = 0 // corrected steps to avoid splitting chars belonging to the same codepoint
|
||||||
moveSteps = if (availableCharacters < -steps) -availableCharacters else steps
|
val text = inputLogic.mConnection.getTextBeforeCursor(-steps * 4, 0) ?: return false
|
||||||
|
loopOverCodePointsBackwards(text) {
|
||||||
|
if (StringUtils.mightBeEmoji(it)) {
|
||||||
|
actualSteps = 0
|
||||||
|
return@loopOverCodePointsBackwards true
|
||||||
|
}
|
||||||
|
actualSteps -= Character.charCount(it)
|
||||||
|
actualSteps <= steps
|
||||||
|
}
|
||||||
|
moveSteps = -text.length.coerceAtMost(abs(actualSteps))
|
||||||
if (moveSteps == 0) {
|
if (moveSteps == 0) {
|
||||||
// some apps don't return any text via input connection, and the cursor can't be moved
|
// some apps don't return any text via input connection, and the cursor can't be moved
|
||||||
// we fall back to virtually pressing the left/right key one or more times instead
|
// we fall back to virtually pressing the left/right key one or more times instead
|
||||||
while (steps != 0) {
|
repeat(-steps) {
|
||||||
onCodeInput(KeyCode.ARROW_LEFT, Constants.NOT_A_COORDINATE, Constants.NOT_A_COORDINATE, false)
|
onCodeInput(KeyCode.ARROW_LEFT, Constants.NOT_A_COORDINATE, Constants.NOT_A_COORDINATE, false)
|
||||||
++steps
|
|
||||||
}
|
}
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
val availableCharacters = inputLogic.mConnection.getTextAfterCursor(64, 0)?.length ?: return false
|
var actualSteps = 0 // corrected steps to avoid splitting chars belonging to the same codepoint
|
||||||
moveSteps = availableCharacters.coerceAtMost(steps)
|
val text = inputLogic.mConnection.getTextAfterCursor(steps * 4, 0) ?: return false
|
||||||
|
loopOverCodePoints(text) {
|
||||||
|
if (StringUtils.mightBeEmoji(it)) {
|
||||||
|
actualSteps = 0
|
||||||
|
return@loopOverCodePoints true
|
||||||
|
}
|
||||||
|
actualSteps += Character.charCount(it)
|
||||||
|
actualSteps >= steps
|
||||||
|
}
|
||||||
|
moveSteps = text.length.coerceAtMost(actualSteps)
|
||||||
if (moveSteps == 0) {
|
if (moveSteps == 0) {
|
||||||
while (steps != 0) {
|
repeat(steps) {
|
||||||
onCodeInput(KeyCode.ARROW_RIGHT, Constants.NOT_A_COORDINATE, Constants.NOT_A_COORDINATE, false)
|
onCodeInput(KeyCode.ARROW_RIGHT, Constants.NOT_A_COORDINATE, Constants.NOT_A_COORDINATE, false)
|
||||||
--steps
|
|
||||||
}
|
}
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
|
|
@ -625,7 +625,7 @@ public final class StringUtils {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
public static boolean mightBeEmoji(final String s) {
|
public static boolean mightBeEmoji(final CharSequence s) {
|
||||||
int offset = 0;
|
int offset = 0;
|
||||||
final int length = s.length();
|
final int length = s.length();
|
||||||
while (offset < length) {
|
while (offset < length) {
|
||||||
|
|
|
@ -109,7 +109,7 @@ fun String.decapitalize(locale: Locale): String {
|
||||||
|
|
||||||
fun isEmoji(c: Int): Boolean = mightBeEmoji(c) && isEmoji(newSingleCodePointString(c))
|
fun isEmoji(c: Int): Boolean = mightBeEmoji(c) && isEmoji(newSingleCodePointString(c))
|
||||||
|
|
||||||
fun isEmoji(s: String): Boolean = mightBeEmoji(s) && s.matches(emoRegex)
|
fun isEmoji(s: CharSequence): Boolean = mightBeEmoji(s) && s.matches(emoRegex)
|
||||||
|
|
||||||
fun String.splitOnWhitespace() = split(whitespaceSplitRegex)
|
fun String.splitOnWhitespace() = split(whitespaceSplitRegex)
|
||||||
|
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue