Как создать горячую клавишу для последовательности вводов при удерживании клавиши Ctrl, например {Ctrl Down}ec{Ctrl Up}?

Как создать горячую клавишу для последовательности вводов при удерживании клавиши Ctrl, например {Ctrl Down}ec{Ctrl Up}?

Например, в Visual Studio Express 2013 многие сочетания клавиш форматирования были «свернуты» в Ctrl+ E. Чтобы прокомментировать выделение, нужно удерживать Ctrl, нажать E, нажать C,затемвыпускать Ctrl.

    введите описание изображения здесь

Если бы я былотправкатакой вклад, я бы написал

SendInput {Ctrl Down}ec{Ctrl Up}

Но как мне сделать эту последовательность горячей клавишей? Я пробовал

{Ctrl Down}EC{Ctrl Up}::
    MsgBox, "Hello, world!"
    Return

но, конечно, это приводит к синтаксической ошибке:

    введите описание изображения здесь

решение1

Наконец-то разобрался.

вкратце

Заменить ^eпервым желаемым нажатием клавиши. Заменить 3индексом ASCII второго желаемого нажатия клавиши. Это обеспечивает удержание Ctrlпри обоих нажатиях клавиш, в противном случае отменяет.

~$Ctrl UP::
    ChordIsBroken := True
    Return
^e::
    ChordIsBroken := False
    Input, OutputVar, L1 M
    If (!ChordIsBroken && Asc(OutputVar) = 3)
    {
        MsgBox "Hello, World!"
    }
    Else
    {
        SendInput %OutputVar%
    }
    Return

Чтобы адаптировать это к Shiftвместо Ctrl, вам придется заменить Ctrls, удалить Mи сделать более простое сравнение, например, OutputVar = Cвместо Asc(OutputVar) = 3. Не уверен, как расширить это до Altи Win, но вам, возможно, придется попробовать L2или что-то в этом роде.

Объяснение

Inputказалось очевидным местом для начала. Inputпросит AHK подождать ввода данных пользователем,например

^e::
    Input, OutputVar, L1        ; "L1" means wait for 1 keystroke then continue.
    If (OutputVar = "c")
    {
        MsgBox, "Hello, World!"
    }
    Return

Окно сообщения выше срабатывает на CtrlE. CНо мы ищем CtrlC, поэтому давайте исправим это:

^e::
    Input, OutputVar, L1 M      ; "M" allows storage of modified keystrokes (^c).
    If (Asc(OutputVar) = 3)     ; ASCII character 3 is ^c.
    {
        MsgBox "Hello, World!"
    }
    Return

Теперь у нас есть окно сообщения при нажатии CtrlEтогда CtrlC. Но есть проблема с этим: окно сообщения срабатывает даже когда явыпускать Ctrlмежду двумя нажатиями клавиш. Так как же мы обнаруживаем по сути {Ctrl Up}средний ввод? Вы не можете просто проверить на входе —

^e::
    if (!GetKeyState("Ctrl"))
    {
        Return
    }
    Input, OutputVar, L1 M 
    ; ...

—также вы не можете просто проверить после ввода—

^e::
    Input, OutputVar, L1 M 
    if (!GetKeyState("Ctrl"))
    {
        Return
    }
    ; ...

— и вы даже не сможете обойтись без обоих вариантов, потому что в любом случае вы пропустите {Ctrl Up}блокировку while для ввода данных.

Затем я посмотрел вдокументация поHotkeyдля вдохновения. Пользовательский оператор комбинирования, &, казался многообещающим. Но, к сожалению,

^e & ^c::
    ; ...

вызвало ошибку компиляции; по-видимому, это &для объединениянемодифицированныйтолько нажатия клавиш.

Наконец, это было до UP, и вот где я наконец совершил прорыв. Я переопределил Ctrl's, UPчтобы установитьпереключатьэто предотвратит срабатывание окна сообщения!

$Ctrl::Send {Ctrl Down}     ; The $ prevents an infinite loop. Although this
$Ctrl UP::                  ; line seems redundant, it is in fact necessary.
    ChordIsBroken := True   ; Without it, Ctrl becomes effectively disabled.
    Send {Ctrl Up}
    Return
^e::
    ChordIsBroken := False
    Input, OutputVar, L1 M
    If (!ChordIsBroken && Asc(OutputVar) = 3)
    {
        MsgBox "Hello, World!"
    }
    Return

Теперь, когда я нажимаю CtrlE, затем отпускаю Ctrlи нажимаю CtrlC, ничего не происходит, как и ожидалось!

Осталось исправить еще одну вещь. При «отмене» («сломанный аккорд») я хотел, чтобы все нажатия клавиш возвращались к норме. Но в коде выше пришлось Inputбы «съесть» нажатие клавиши перед возвратом, независимо от сломленного аккорда или нерелевантного вторичного нажатия клавиши. Добавление случая Elseпрекрасно решает эту проблему:

    Else
    {
        SendInput %OutputVar%
    }

Итак, вот вам «аккорды» в AutoHotkey. (Хотя я бы не назвал это «аккордом». Скореемелодия, с басовой партией ;-)


@hippibruder великодушно указывает, что я могу избежать определения, $Ctrl::используя ~для создания $Ctrl UP::неблокируемого. Это позволяет немного упростить! (См. раздел tl;dr вверху для получения окончательного результата.)


Еще одно. Если, возможно, при "отмене" ("сломанный аккорд") вы захотите выдать первое нажатие клавиши,то есть CtrlEпросто добавьте его в Elseблок,

    Else
    {
        SendInput ^e
        SendInput %OutputVar%
    }

и не забудьте изменить горячую клавишу на

$^e::

чтобы избежать бесконечного цикла.

решение2

Я не думаю, что в AHK есть встроенная поддержка аккордов клавиш. Один из способов их обнаружения — зарегистрировать горячую клавишу для первой клавиши в аккорде (^e), а затем использовать Input-Command для обнаружения следующих клавиш.

; Tested with AHK_L U64 v1.1.14.03 (and Visual Studio 2010)
; This doesn't block the input. To block it remove '~' from the hotkey and 'V' from Input-Cmd
~^e::
  ; Input-Cmd will capture the next keyinput as its printable representation. 
  ; (i.e. 'Shift-a' produces 'A'. 'a' produces 'a'. 'Ctrl-k' produces nothing printable. This can be changed with 'M' option. Maybe better approch; See help) 
  ; Only the next, because of 'L1'. (Quick-Fail; Not necessary)
  ; To detect 'c' and 'u' with control pressed I used them as EndKeys.
  ; If a EndKey is pressed the Input-Cmd will end and save the EndKey in 'ErrorLevel'.
  Input, _notInUse, V L1 T3, cu

  ; Return if Input-Cmd was not terminated by an EndKey
  ; or 'Control' is no longer pressed. (It would be better if Input-Cmd would be also terminated by a {Ctrl Up}. I don't know if that is possible)
  if ( InStr(ErrorLevel, "Endkey:") != 1
    || !GetKeyState("Control") )
    {
    return
    }

  ; Extract the EndKey-Name from 'ErrorLevel' (ErrorLevel == "Endkey:c")
  key := SubStr(ErrorLevel, 8)    
  if ( InStr(key, "c") == 1 )
    {
    TrayTip,, ^ec
    }
  else if ( InStr(key, "u") == 1 )
    {
    TrayTip,, ^eu
    }
  else
    {
    MsgBox, wut? key="%key%"
    }
return

Связанный контент