如何在按住 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

终于明白了。

总结

用第一个所需击键替换^e3用第二个所需击键的 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}输入的阻塞。

然后我调查了文档Hotkey寻找灵感。自定义组合运算符&看起来很有希望。但不幸的是,

^e & ^c::
    ; ...

导致编译错误;显然&是用于合并未修改仅限按键。

最后,它取决于UP,这就是我最终取得突破的地方。我重新定义Ctrl了 ,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 部分以了解最终结果。)


还有一件事。如果碰巧在“取消”(“断弦”)时,你想发出第一个按键,IE CtrlE单独地,只需将其添加到Else块中,

    Else
    {
        SendInput ^e
        SendInput %OutputVar%
    }

别忘了将热键改为

$^e::

以避免无限循环。

答案2

我认为 AHK 中没有内置对键和弦的支持。检测这些键和弦的一种方法是注册和弦中第一个键的热键 (^e),然后使用输入命令检测下一个键。

; 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

相关内容