¿Cómo creo una tecla de acceso rápido para una secuencia de entradas mientras mantengo Ctrl, por ejemplo, {Ctrl Abajo}ec{Ctrl Arriba}?

¿Cómo creo una tecla de acceso rápido para una secuencia de entradas mientras mantengo Ctrl, por ejemplo, {Ctrl Abajo}ec{Ctrl Arriba}?

Por ejemplo, en Visual Studio Express 2013, muchos de los atajos de formato se han "doblado" en Ctrl+ E. Para comentar una selección, se debe mantener presionado Ctrl, presionar E, presionar C,entoncesliberar Ctrl.

    ingrese la descripción de la imagen aquí

Si yo fueraenviandotal entrada, escribiría

SendInput {Ctrl Down}ec{Ctrl Up}

Pero, ¿cómo convierto esa secuencia en una tecla de acceso rápido? Lo intenté

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

pero claro, esto provoca un error de sintaxis:

    ingrese la descripción de la imagen aquí

Respuesta1

Finalmente lo descubrí.

tl; dr

Reemplace ^econ la primera pulsación de tecla deseada. Reemplace 3con el índice ASCII de la segunda pulsación de tecla deseada. Esto hace cumplir lo que Ctrlse mantiene mediante ambas pulsaciones de teclas; de lo contrario, se 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 esto Shiften lugar de Ctrl, tendría que reemplazar Ctrls, eliminar My hacer una comparación más simple como OutputVar = Cen lugar de Asc(OutputVar) = 3. No estoy seguro de cómo extender esto a Altpero Wines posible que tengas que intentarlo L2o algo por el estilo.

Explicación

InputParecía un lugar obvio para comenzar. Inputle pide a AHK que espere la entrada del usuario,p.ej

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

El cuadro de mensaje de arriba se activa CtrlEentonces C. Pero estamos buscando CtrlC, así que arreglemos esto:

^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

Ahora tenemos un cuadro de mensaje al presionar CtrlEluego CtrlC. Pero hay un problema con esto: el cuadro de mensaje se activa incluso cuandoliberar Ctrlentre las dos pulsaciones de teclas. Entonces, ¿cómo detectamos esencialmente una {Ctrl Up}entrada intermedia? No puedes simplemente comprobar la entrada.

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

—tampoco puede simplemente verificar después de ingresar—

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

—Ni siquiera puedes salirte con la tuya haciendo ambas cosas, porque pase lo que pase, te perderás el {Ctrl Up}momento de bloquear la entrada.

Entonces mirédocumentación sobreHotkeypor inspiración. El operador de combinación personalizado, &parecía prometedor. Pero desafortunadamente,

^e & ^c::
    ; ...

provocó un error de compilación; aparentemente el &es para combinarsin modificarpulsaciones de teclas, únicamente.

Finalmente, le tocó a UP, y ahí es donde finalmente logré el gran avance. Redefiní Ctrl's UPpara establecer unpalanca¡Eso evitaría que se active el cuadro de mensaje!

$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

Ahora, cuando presiono CtrlE, luego suelto Ctrly presiono CtrlC, ¡no sucede nada, como se esperaba!

Había una última cosa que arreglar. En "cancelación" (un "acorde roto"), quería que todas las pulsaciones de teclas volvieran a la normalidad. Pero en el código anterior, Inputtendría que "comerse" una pulsación de tecla antes de regresar, independientemente de un acorde roto o una pulsación de tecla secundaria irrelevante. Agregar un Elsecaso resuelve muy bien esto:

    Else
    {
        SendInput %OutputVar%
    }

Ahí lo tienes: "acordes" en AutoHotkey. (Aunque no llamaría a esto exactamente un "acorde". Más bien unmelodía, con línea de bajo ;-)


@hippibruder señala generosamente que puedo evitar definir $Ctrl::usando ~para hacer $Ctrl UP::sin bloqueo. ¡Esto permite cierta simplificación! (Consulte la sección tl;dr en la parte superior para conocer el resultado final).


Una cosa más. Si, por casualidad, al momento de una "cancelación" (un "acorde roto"), desea emitir la primera pulsación de tecla,es decir CtrlEpor sí solo, simplemente agréguelo en el Elsebloque,

    Else
    {
        SendInput ^e
        SendInput %OutputVar%
    }

y no olvides cambiar la tecla de acceso rápido a

$^e::

para evitar un bucle infinito.

Respuesta2

No creo que haya soporte integrado para acordes clave en AHK. Una forma de detectarlos sería registrar una tecla de acceso rápido para la primera tecla del acorde (^e) y luego usar el comando de entrada para detectar las siguientes 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

información relacionada