Skip to content
CleverKeys Wiki
implemented v2.0.0 development User guide

Subkey Popover Technical Specification

Overview

Holding an eligible key past the long-press timeout, without moving, opens a 3×3 grid centred on the finger: the eight subkey slots around a neutral centre that shows the held key. Releasing on a slot emits it through the same path as a short swipe; releasing on an empty slot opens an assign screen; resting on an assigned slot for 3 s opens an edit screen. The internal engineering note is subkey-popover.md.

Key Components

ComponentFilePurpose
SubkeyPopoverGeometrypopover/SubkeyPopoverGeometry.ktPure: slot hit-test (slotAt) and grid clamping inside the view (gridCentre)
SubkeyPopoverSlotspopover/SubkeyPopoverSlots.ktPure: resolves the eight slots from custom mappings and modified layout subkeys
SubkeyPopoverStatepopover/SubkeyPopoverState.ktPer-pointer state; DWELL_EDIT_MS = 3_000L, DWELL_RING_START_MS = 800L
SubkeyPopoverRendererpopover/SubkeyPopoverRenderer.ktDraws the open popover last in Keyboard2View.onDraw
SubkeyAssignActivitypopover/SubkeyAssignActivity.ktTranslucent dialog activity for assign/edit
PointersPointers.kt (handleLongPress, isSubkeyPopoverKey, tryOpenSubkeyPopover)Opens, tracks and resolves the popover
SubkeyPopoverMigrationConfig.ktSeeds the setting OFF once for upgraded installs (config v5)
GestureTuningSectionui/settings/sections/GestureTuningSection.ktToggle and the two neutral-zone sliders

Behaviour

  1. Open. In handleLongPress, after the TrackPoint and backspace selection-delete branches, the popover is tried when subkey_popover_enabled is set and the finger moved no more than the hold-stillness threshold:

    // Hold-then-select subkey popover: replaces key repeat on the keys it applies to.
    if (snap.subkey_popover_enabled && movementDist <= HOLD_STILLNESS_PX && tryOpenSubkeyPopover(ptr, snap)) {
        return
    }
  2. Eligible keys (isSubkeyPopoverKey): a Char or String value that is not blank and not special, not a modifier (latchable or latched), not backspace, no navigation subkeys, and not remapped by KeyModifier.modify_long_press. A key with no mappable key code and only empty slots keeps its old hold behaviour.

  3. Slots. Each slot is the user’s custom mapping if any, else the layout subkey with the current modifiers applied, else empty. A mapping to the removed placeholder counts as empty.

  4. Selection. Displacement from the grid centre, inside the neutral rectangle (width and height as % of the key), selects nothing. Outside it, the 45° sector of the displacement, normalised by the cell size, selects a slot. Selection waits until the finger has moved (15 % of a cell), so an unmoved release never emits anything.

  5. Release. Neutral: nothing. Default subkey: emitted as a short swipe would emit it. Custom mapping: executed. Empty slot: assign screen.

  6. Dwell. From 0.8 s on an assigned slot a progress border runs around the whole popover; at 3 s the popover closes and the edit screen opens; the following release does nothing.

  7. Key repeat. On eligible keys the popover replaces key repeat while enabled.

Assign / Edit Screens

SubkeyAssignActivity reuses CommandPaletteDialog, the shared ShortSwipeAssignment.apply(...) and ShortSwipeCustomizationManager, so popover slots and short-swipe mappings are one store.

ScreenContents
Assign, plain empty slotOpens the palette directly
Assign, layout subkey removedSlot details, Choose an action, Restore default
EditSlot details; Edit (custom mappings except raw key events; palette opens in that action’s editor via initialMapping), Reassign, Restore default (custom mapping over a layout subkey), Remove (error colour)

Remove deletes a custom mapping over an empty slot; over a layout subkey it stores the removed placeholder.

Configuration

SettingKeyDefaultRangeSource
Subkey popover on holdsubkey_popover_enabledtrue (upgrades seeded false)BooleanConfig.kt Defaults.SUBKEY_POPOVER_ENABLED
Neutral zone widthsubkey_popover_neutral_width6020–150 % of key widthDefaults.SUBKEY_POPOVER_NEUTRAL_WIDTH
Neutral zone heightsubkey_popover_neutral_height6020–150 % of key heightDefaults.SUBKEY_POPOVER_NEUTRAL_HEIGHT

SubkeyPopoverMigration.seedsOff returns true for a non-fresh install whose saved config version is below 5 and that has no explicit value for the key (a value restored from a backup is kept). Settings are read through ConfigSnapshot, captured at touch-down.

Test Coverage

SuiteFile
Pure JVMsrc/test/kotlin/tribixbite/cleverkeys/popover/SubkeyPopoverGeometryTest.kt
Pure JVMsrc/test/kotlin/tribixbite/cleverkeys/popover/SubkeyPopoverSlotsTest.kt
Pure JVMsrc/test/kotlin/tribixbite/cleverkeys/SubkeyPopoverMigrationTest.kt
Mocksrc/test/kotlin/tribixbite/cleverkeys/PointersSubkeyPopoverTest.kt