如何在按住 Ctrl 的同时为一系列输入创建热键,例如 {Ctrl Down}ec{Ctrl Up}?

And*_*ong 7 autohotkey

例如,在 Visual Studio Express 2013 中,许多格式化快捷方式已“折叠”到Ctrl+ 中E。要评论选择,可以按住Ctrl、点击E、点击C然后松开Ctrl

    在此处输入图片说明

如果我发送这样的输入,我会写

SendInput {Ctrl Down}ec{Ctrl Up}
Run Code Online (Sandbox Code Playgroud)

但是如何将该序列变成热键呢?我试过

{Ctrl Down}EC{Ctrl Up}::
    MsgBox, "Hello, world!"
    Return
Run Code Online (Sandbox Code Playgroud)

但当然,这会导致语法错误:

    在此处输入图片说明

And*_*ong 7

终于想通了。

tl;博士

替换^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
Run Code Online (Sandbox Code Playgroud)

要将其调整为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
Run Code Online (Sandbox Code Playgroud)

上面的消息框在CtrlEthen上触发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
Run Code Online (Sandbox Code Playgroud)

现在我们在按下CtrlEthen 时有一个消息框CtrlC。但这有一个问题:即使我在两次击键之间松开 Ctrl,消息框也会触发。那么,我们如何本质上检测{Ctrl Up}中间输入?你不能只检查入口——

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

——也不能仅仅在输入后检查——

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

——你甚至不能同时做这两件事,因为不管怎样,你都会错过{Ctrl Up}while 阻塞输入。

然后我查看了文档以Hotkey获得灵感。自定义组合运算符&,看起来很有前途。但遗憾的是,

^e & ^c::
    ; ...
Run Code Online (Sandbox Code Playgroud)

导致编译错误;显然,仅&用于组合未修改的按键。

终于到了UP,这就是我最终取得突破的地方。我重新定义了Ctrl'sUP来设置一个可以防止消息框触发的切换

$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
Run Code Online (Sandbox Code Playgroud)

现在,当我按下CtrlE,然后松开Ctrl,然后按下CtrlC,没有任何反应,正如预期的那样!

还有最后一件事需要解决。在“取消”(“断弦”)时,我希望所有击键都恢复正常。但是在上面的代码中Input,无论是和弦断裂还是无关的辅助击键,都必须在返回之前“吃掉”一次击键。添加Else案例很好地解决了这个问题:

    Else
    {
        SendInput %OutputVar%
    }
Run Code Online (Sandbox Code Playgroud)

所以,你有它 - AutoHotkey 中的“和弦”。(虽然,我不会完全称其为“和弦”。更像是带有低音线的旋律;-)


@hippibruder 慷慨地指出,我可以$Ctrl::通过使用~使$Ctrl UP::非阻塞来避免定义。这允许一些简化!(有关最终结果,请参阅顶部的 tl;dr 部分。)


还有一件事。如果,也许,在“取消”(“断弦”)时,您想发出第一次击键, 单独发出CtrlE,只需将其添加到Else块中,

    Else
    {
        SendInput ^e
        SendInput %OutputVar%
    }
Run Code Online (Sandbox Code Playgroud)

并且不要忘记将热键更改为

$^e::
Run Code Online (Sandbox Code Playgroud)

以避免无限循环。