Como eu crio uma tecla de atalho para uma seqüência de entradas enquanto pressiono Ctrl, por exemplo? {Ctrl Down} ec {Ctrl para cima}?

7

Por exemplo, no Visual Studio Express 2013, muitos dos atalhos de formatação foram "dobrados" em Ctrl + E . Para comentar uma seleção, deve-se pressionar Ctrl , teclar E , pressionar C , então liberar Ctrl .

Seeuestivesseenviandoessaentrada,euescreveria

SendInput{CtrlDown}ec{CtrlUp}

Mascomofaçoessasequênciaemumatecladeatalho?Eutentei

{CtrlDown}EC{CtrlUp}::MsgBox,"Hello, world!"
    Return

mas, claro, isso causa um erro de sintaxe:

    
por Andrew Cheong 04.03.2014 / 20:43

2 respostas

7

Finalmente descobri.

tl; dr

Substitua ^e pelo primeiro pressionamento de tecla desejado. Substitua 3 pelo índice ASCII do segundo pressionamento de tecla desejado. Isso impõe que a Ctrl seja mantida através de ambos os pressionamentos de tecla, caso contrário, cancela.

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

Para adaptar isso a Shift ao invés de Ctrl , você teria que substituir o Ctrl s, remover o M e fazer um comparação mais simples como OutputVar = C em vez de Asc(OutputVar) = 3 . Não sei como estender isso para Alt e Win , mas você pode ter que tentar L2 ou algo do tipo.

Explicação

Input parecia um lugar óbvio para começar. Input pede ao AHK que aguarde a entrada do usuário, por exemplo.

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

A caixa de mensagem acima dispara em Ctrl E e depois em C . Mas nós estamos procurando Ctrl C , então vamos corrigir isso:

^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

Agora temos uma caixa de mensagem ao pressionar Ctrl E e depois Ctrl C . Mas há um problema com isso: a caixa de mensagem é acionada mesmo quando eu libero Ctrl entre os dois pressionamentos de tecla. Então, como detectamos essencialmente uma {Ctrl Up} mid-input? Você não pode simplesmente verificar a entrada -

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

—não pode simplesmente verificar após a entrada -

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

- você não pode nem mesmo fazer as duas coisas, porque não importa o quê, você perderá o {Ctrl Up} enquanto bloqueia a entrada.

Depois, procurei na documentação sobre Hotkey como inspiração. O operador de combinação personalizada, & , parecia promissor. Mas infelizmente,

^e & ^c::
    ; ...

causou um erro de compilação; aparentemente, o & é para combinar apenas as teclas não modificadas .

Finalmente, foi até UP , e é aí que eu finalmente fiz a descoberta. Redefinei UP do Ctrl para definir um alternância que impediria o acionamento da caixa de mensagem!

$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

Agora, quando pressionar Ctrl E , solte a Ctrl e pressione Ctrl C , nada acontece, como esperado!

Houve uma última coisa para corrigir. Em "cancelamento" (um "acorde quebrado"), eu queria que todos os toques de tecla voltassem ao normal. Mas no código acima, o Input teria que "comer" um pressionamento de tecla antes de retornar, independentemente de um acorde quebrado ou de um pressionamento de tecla secundário irrelevante. Adicionar um Else resolve bem isso:

    Else
    {
        SendInput %OutputVar%
    }

Então, você tem isso - "acordes" no AutoHotkey. (Apesar de eu não chamar exatamente isso de "acorde". Mais como uma melodia , com uma linha de baixo; -)

@hippibruder generosamente aponta que eu posso evitar definir $Ctrl:: usando ~ para tornar $Ctrl UP:: sem bloqueio. Isso permite alguma simplificação! (Veja a seção dr no topo para o resultado final.)

Mais uma coisa. Se, por acaso, sobre "cancelamento" (um "acorde quebrado"), você gostaria de emitir o primeiro toque de tecla, ie Ctrl E por conta própria, basta adicionar isso no bloco Else ,

    Else
    {
        SendInput ^e
        SendInput %OutputVar%
    }

e não se esqueça de mudar a tecla de atalho para

$^e::

para evitar um loop infinito.

    
por 06.03.2014 / 05:01
2

Eu não acho que haja suporte embutido para os principais acordes no AHK. Uma maneira de detectá-los seria registrar uma tecla de atalho para a primeira tecla no acorde (^ e) e usar o comando de entrada para detectar as próximas teclas.

; 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
    
por 05.03.2014 / 17:23

Tags