![如何在按住 Ctrl 键的同时为一系列输入创建热键,例如 {Ctrl Down}ec{Ctrl Up}?](https://linux22.com/image/1400173/%E5%A6%82%E4%BD%95%E5%9C%A8%E6%8C%89%E4%BD%8F%20Ctrl%20%E9%94%AE%E7%9A%84%E5%90%8C%E6%97%B6%E4%B8%BA%E4%B8%80%E7%B3%BB%E5%88%97%E8%BE%93%E5%85%A5%E5%88%9B%E5%BB%BA%E7%83%AD%E9%94%AE%EF%BC%8C%E4%BE%8B%E5%A6%82%20%7BCtrl%20Down%7Dec%7BCtrl%20Up%7D%EF%BC%9F.png)
例如,在 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,您必须替换Ctrl
s ,删除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