From 9f6ab1918559e0d039adfbba0963862d9c019d03 Mon Sep 17 00:00:00 2001 From: Zokii0 Date: Fri, 16 Oct 2026 22:04:08 +0200 Subject: [PATCH] Delete swipe: larger deselect step for right swipe after a vertical swipe --- .../keyboard/KeyboardActionListenerImpl.kt | 33 +++++++++++++++++-- .../keyboard/keyboard/WordDeleteSwipeTest.kt | 31 +++++++++++++++++ 2 files changed, 62 insertions(+), 2 deletions(-) diff --git a/app/src/main/java/helium314/keyboard/keyboard/KeyboardActionListenerImpl.kt b/app/src/main/java/helium314/keyboard/keyboard/KeyboardActionListenerImpl.kt index c9876964e..09876451d 100644 --- a/app/src/main/java/helium314/keyboard/keyboard/KeyboardActionListenerImpl.kt +++ b/app/src/main/java/helium314/keyboard/keyboard/KeyboardActionListenerImpl.kt @@ -569,7 +569,11 @@ class KeyboardActionListenerImpl(private val latinIME: LatinIME, private val inp } if (stepsX != 0) { - val actual = actualSteps(stepsX) + val scaledStepsX = scaleDeleteSwipeDeselectSteps( + stepsX, deleteSwipeLineIndex, deleteSwipeCharOffset, + DELETE_SWIPE_DESELECT_CHAR_MULTIPLIER, DELETE_SWIPE_DESELECT_CHAR_TOLERANCE + ) + val actual = actualSteps(scaledStepsX) // Positive offset: selection start is left of the line start (selected more by swiping left). // Negative offset: selection start is right of the line start (deselected by swiping right // after a vertical swipe). It can never go past the position where the swipe started. @@ -616,7 +620,11 @@ class KeyboardActionListenerImpl(private val latinIME: LatinIME, private val inp val minIndex = -getWordBoundariesForwards( deleteSwipeInitialText, deleteSwipeSelectionAnchor(), deleteSwipeLineBase() ).size - deleteSwipeWordIndex = (deleteSwipeWordIndex - stepsX).coerceAtLeast(minIndex) + val scaledStepsX = scaleDeleteSwipeDeselectSteps( + stepsX, deleteSwipeLineIndex, deleteSwipeWordIndex, + DELETE_SWIPE_DESELECT_WORD_MULTIPLIER, DELETE_SWIPE_DESELECT_WORD_TOLERANCE + ) + deleteSwipeWordIndex = (deleteSwipeWordIndex - scaledStepsX).coerceAtLeast(minIndex) } if (deleteSwipeLineIndex != oldLineIndex || deleteSwipeWordIndex != oldWordIndex) { @@ -1047,6 +1055,27 @@ class KeyboardActionListenerImpl(private val latinIME: LatinIME, private val inp private fun Int.isMetaLock() = this == KeyCode.CTRL_LOCK || this == KeyCode.ALT_LOCK || this == KeyCode.FN_LOCK || this == KeyCode.META_LOCK + /** Chars deselected per horizontal step when swiping right after a (nearly) vertical-only delete swipe. */ + internal const val DELETE_SWIPE_DESELECT_CHAR_MULTIPLIER = 4 + /** Words deselected per horizontal step when swiping right after a (nearly) vertical-only delete swipe. */ + internal const val DELETE_SWIPE_DESELECT_WORD_MULTIPLIER = 2 + /** Max chars selected to the left of the line start that still count as "nearly vertical" (slight diagonal). */ + internal const val DELETE_SWIPE_DESELECT_CHAR_TOLERANCE = 4 + /** Max words selected to the left of the line start that still count as "nearly vertical" (slight diagonal). */ + internal const val DELETE_SWIPE_DESELECT_WORD_TOLERANCE = 1 + + /** + * Rightward steps get larger only after a (nearly) vertical-only swipe: the selection base is on a + * line above the swipe start ([lineIndex] > 0) and at most [tolerance] chars/words were added to the + * left of that line start ([horizontalOffset] <= [tolerance]). In that case the finger is still close + * to the right screen edge (the delete key is on the right) and has little room for the right swipe. + * Unchanged: leftward steps, the first line (no vertical swipe, e.g. horizontal-left then right), + * and sharper diagonals, where the finger already has room and precise control is more useful. + */ + internal fun scaleDeleteSwipeDeselectSteps( + stepsX: Int, lineIndex: Int, horizontalOffset: Int, multiplier: Int, tolerance: Int + ): Int = if (stepsX > 0 && lineIndex > 0 && horizontalOffset <= tolerance) stepsX * multiplier else stepsX + internal fun getLineBoundariesBackwards(text: String, endOffset: Int): List { val boundaries = mutableListOf() boundaries.add(endOffset) diff --git a/app/src/test/java/helium314/keyboard/keyboard/WordDeleteSwipeTest.kt b/app/src/test/java/helium314/keyboard/keyboard/WordDeleteSwipeTest.kt index 6a7053527..4f7b3609b 100644 --- a/app/src/test/java/helium314/keyboard/keyboard/WordDeleteSwipeTest.kt +++ b/app/src/test/java/helium314/keyboard/keyboard/WordDeleteSwipeTest.kt @@ -160,4 +160,35 @@ class WordDeleteSwipeTest { KeyboardActionListenerImpl.getWordBoundariesForwards(text, endOffset, startOffset) ) } + + @Test + fun `rightward step is scaled after a vertical-only swipe`() { + // on a line above the swipe start, nothing selected to the left of the line start + assertEquals(4, KeyboardActionListenerImpl.scaleDeleteSwipeDeselectSteps(1, 1, 0, 4, 4)) + assertEquals(12, KeyboardActionListenerImpl.scaleDeleteSwipeDeselectSteps(3, 2, 0, 4, 4)) + // already deselecting inside the line (negative offset) + assertEquals(8, KeyboardActionListenerImpl.scaleDeleteSwipeDeselectSteps(2, 1, -5, 4, 4)) + } + + @Test + fun `rightward step is scaled after a slight diagonal swipe`() { + // up to the tolerance (inclusive) still counts as nearly vertical + assertEquals(4, KeyboardActionListenerImpl.scaleDeleteSwipeDeselectSteps(1, 1, 2, 4, 4)) + assertEquals(4, KeyboardActionListenerImpl.scaleDeleteSwipeDeselectSteps(1, 1, 4, 4, 4)) + // word mode: one word to the left is tolerated + assertEquals(2, KeyboardActionListenerImpl.scaleDeleteSwipeDeselectSteps(1, 1, 1, 2, 1)) + } + + @Test + fun `rightward step is not scaled in other cases`() { + // first line, no vertical swipe (e.g. horizontal-left then right) + assertEquals(1, KeyboardActionListenerImpl.scaleDeleteSwipeDeselectSteps(1, 0, 0, 4, 4)) + assertEquals(1, KeyboardActionListenerImpl.scaleDeleteSwipeDeselectSteps(1, 0, 6, 4, 4)) + // sharper diagonal: more than the tolerance selected to the left + assertEquals(1, KeyboardActionListenerImpl.scaleDeleteSwipeDeselectSteps(1, 1, 5, 4, 4)) + assertEquals(1, KeyboardActionListenerImpl.scaleDeleteSwipeDeselectSteps(1, 1, 2, 2, 1)) + // leftward steps are never scaled + assertEquals(-2, KeyboardActionListenerImpl.scaleDeleteSwipeDeselectSteps(-2, 1, 0, 4, 4)) + assertEquals(0, KeyboardActionListenerImpl.scaleDeleteSwipeDeselectSteps(0, 1, 0, 4, 4)) + } }