Home / Specs / Selection-Delete Mode
Gestures v1.2.4

Selection-Delete Mode

Text selection via backspace swipe-hold gesture

Selection-Delete Mode

Overview

Selection-Delete Mode is a gesture that enables text selection by swiping and holding on the backspace key. When activated, horizontal finger movement selects characters (left/right), vertical movement selects lines (up/down), and releasing the finger deletes all selected text. This provides a single fluid gesture for rapid text correction.

Key Files

FileClass/FunctionPurpose
src/main/kotlin/tribixbite/cleverkeys/Pointers.kthandleSelectionDeleteRepeat()Main selection logic, repeat handler
src/main/kotlin/tribixbite/cleverkeys/Pointers.ktFLAG_P_SELECTION_DELETE_MODEMode state flag (value: 128)
src/main/kotlin/tribixbite/cleverkeys/Config.ktselection_delete_vertical_thresholdVertical activation threshold
src/main/kotlin/tribixbite/cleverkeys/Config.ktselection_delete_vertical_speedVertical speed multiplier
src/main/kotlin/tribixbite/cleverkeys/SettingsActivity.ktSettings UIThreshold and speed sliders

Architecture

User Input (backspace key short swipe + hold)
       |
       v
+------------------+
| onTouchDown()    | -- Defers backspace for gesture detection
+------------------+
       |
       v
+------------------+
| Short Swipe      | -- Detects initial swipe direction
| Detection        |
+------------------+
       |
       v (if hold detected after swipe)
+------------------+
| FLAG_P_SELECTION | -- Sets mode flag, records activation center
| _DELETE_MODE     |
+------------------+
       |
       v
+------------------+
| handleSelection  | -- Timer-based repeat, sends Shift+Arrow keys
| DeleteRepeat()   |
+------------------+
       |
       v (on finger release)
+------------------+
| Delete selected  | -- Sends DEL key to remove selection
| text             |
+------------------+

Data Flow

- Horizontal (dx): Shift+Left or Shift+Right for character selection

- Vertical (dy): Shift+Up or Shift+Down for line selection (if threshold met)

Configuration

KeyTypeDefaultRangeDescription
selection_delete_vertical_thresholdInt4020-80% of horizontal distance required for vertical selection
selection_delete_vertical_speedFloat0.40.1-1.0Speed multiplier for vertical selection

Public API

Pointers.kt

// State flag constant
private const val FLAG_P_SELECTION_DELETE_MODE = 128

// Timer identifier for repeat handler
private val selectionDeleteWhat = Any()

// Main selection handler - called repeatedly while mode active
private fun handleSelectionDeleteRepeat(ptr: Pointer) {
    val dx = ptr.currentX - ptr.selectionCenterX
    val dy = ptr.currentY - ptr.selectionCenterY

    // Horizontal selection (character by character)
    if (abs(dx) > threshold) {
        val key = if (dx > 0) DPAD_RIGHT else DPAD_LEFT
        sendShiftArrow(key)
    }

    // Vertical selection (line by line) with configurable threshold
    if (abs(dy) > verticalThreshold) {
        val key = if (dy > 0) DPAD_DOWN else DPAD_UP
        sendShiftArrow(key, speedMultiplier = verticalSpeed)
    }
}

// Helper to send Shift+Arrow for selection
private fun sendShiftArrow(keyCode: Int, speedMultiplier: Float = 1.0f)

// Helper to add Shift modifier to key
private fun with_extra_mod(value: KeyValue, extra: Modifier): KeyValue

Implementation Details

Activation Sequence

Bidirectional Movement

Selection direction changes dynamically based on current finger position relative to activation center:

// Direction determined each repeat cycle
val direction = when {
    dx > 0 -> DPAD_RIGHT  // Finger right of center
    dx < 0 -> DPAD_LEFT   // Finger left of center
    else -> null
}

This means:

Independent Axis Handling

X and Y axes fire independently, allowing diagonal selection:

// Both axes checked independently each repeat
if (abs(dx) > horizontalThreshold) {
    sendShiftArrow(horizontalKey)
}
if (abs(dy) > verticalThreshold) {
    sendShiftArrow(verticalKey)
}

Shift State Management

Selection requires holding Shift while sending arrow keys:

private fun sendShiftArrow(keyCode: Int) {
    // Create internal Shift modifier
    val shift = makeInternalModifier(Modifier.SHIFT)

    // Apply Shift to arrow key
    val arrowKey = KeyValue.getKeyByCode(keyCode)
    val shiftedArrow = with_extra_mod(arrowKey, Modifier.SHIFT)

    // Send the combined key event
    sendKeyEvent(shiftedArrow)
}

Vertical Threshold Calculation

Vertical movement requires meeting a threshold relative to horizontal movement:

val verticalThreshold = config.selectionDeleteVerticalThreshold / 100f
val horizontalDistance = abs(dx)
val verticalDistance = abs(dy)

// Vertical only fires if it exceeds threshold percentage of horizontal
val shouldFireVertical = verticalDistance > (horizontalDistance * verticalThreshold)

Error Handling