Skip to content

Хоткей коррекции на одиночное нажатие модификатора (например, Control) #16

Open

Description

@8ui

Что хочется

Сделать возможным хоткей коррекции из одиночного нажатия модификатора, в первую очередь Control: нажал и отпустил, без других клавиш между ними. Сочетания вроде Ctrl+C и Ctrl+Space должны работать как раньше.

Зачем

  • Одна клавиша под мизинцем — самый быстрый способ «исправить последнее слово». Так привыкли пользователи Punto Switcher и Caramba Switcher.
  • Комбинации с пробелом печатают символ. Event tap работает в .listenOnly и не может проглотить нажатие. Например, Option+Space на macOS вставляет неразрывный пробел. SwitchFix стирает N символов, но первым под удаление попадает этот пробел, и первая буква слова остаётся: ghbdtn → gпривет. Одиночный модификатор ничего не печатает, так что такой проблемы у него нет.
  • Сейчас так настроить нельзя. Окно настроек не принимает модификатор без основной клавиши, а KeyboardMonitor.classify обрабатывает flagsChanged только для CapsLock (хоткей отмены).

Как это может работать

  • На flagsChanged левого или правого Control (keyCode 59/62) с установленным .maskControl и без других модификаторов — «взвести» тап.
  • Любое другое событие (keyDown, другой модификатор, клик мышью) — снять взвод.
  • На отпускании Control: если тап взведён — вернуть .hotkey.

Прототип, который работает у меня локально. Он включается, когда hotkeyKeyCode == 59, а hotkeyModifiers == 0, и значения выставляются через defaults write, потому что UI их не умеет:

+    // Tap-callback-thread confined: a lone Control press awaiting its release.
+    private var controlTapArmed = false
...
+    private static let leftControlKeyCode: UInt16 = 59
+    private static let rightControlKeyCode: UInt16 = 62
...
     ) -> CapturedInput.Kind? {
+        let hotkeys = captureState.hotkeyConfiguration()
+        let isControlKey = type == .flagsChanged &&
+            (keyCode == KeyboardMonitor.leftControlKeyCode || keyCode == KeyboardMonitor.rightControlKeyCode)
+        if !isControlKey {
+            controlTapArmed = false
+        }
+        // Hotkey key code = Control: fire on a lone Control tap (press + release with
+        // nothing else in between), so Ctrl+<key> combos keep working as usual.
+        if isControlKey, hotkeys.hotkeyKeyCode == KeyboardMonitor.leftControlKeyCode {
+            if flags.contains(.maskControl) {
+                controlTapArmed = flags.intersection([.maskCommand, .maskAlternate, .maskShift]).isEmpty
+                return nil
+            }
+            let fire = controlTapArmed
+            controlTapArmed = false
+            return fire ? .hotkey : nil
+        }
+
         if type == .leftMouseDown || type == .rightMouseDown || type == .otherMouseDown {
             return .focusMayChange
         }
defaults write com.switchfix.app SwitchFix_hotkeyKeyCode -int 59
defaults write com.switchfix.app SwitchFix_hotkeyModifiers -int 0

Для полноценной фичи нужно ещё:

  • научить окно настроек записывать одиночный модификатор, лучше сразу Control / Option / Shift / Command, с выбором левого, правого или любого;
  • отображать такой хоткей в UI, сейчас он показывается некорректно;
  • возможно, добавить таймаут: не срабатывать, если Control держали дольше ~0,5 с.

Окружение

SwitchFix v0.0.9 (d31f7b6), macOS 27.0, Apple Silicon, Swift 6.4.

Activity

  1. Hantok commented on Oct 20, 2026

    @Hantok
    Member

    Pre-merge audit against released v0.0.11 and PR #22 (commit 68873cc): not implemented. No fixed release yet.

    KeyboardMonitor.classify still handles modifier flagsChanged events only for the Caps Lock revert shortcut. The settings recorder also passes through other modifier transitions, so a lone Control/Option/Shift/Command cannot be recorded or triggered.

    Proposed implementation:

    1. Add press/release tracking for modifier-only correction shortcuts, including left/right/any-side selection.
    2. Cancel an armed tap on another key, another modifier, mouse click, or event-tap reset; preserve normal modifier combinations. Define and test a maximum hold duration.
    3. Extend shortcut recording, persistence, display, and conflict handling for these shortcuts while preserving existing saved combinations.
    4. Cover lone taps, both sides, chords, long holds, cancellation, and duplicate events with deterministic tests; verify on macOS that no extra character is inserted.

    Leaving open. This needs a separate feature change; PR #22 does not address it.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions