When I started with AutoHotkey, I followed Chris's example for multi-tap hotkeys as demonstrated in Example #3 of the SetTimer documentation. While the example given is good to demonstrate the possibilites of settimer, it's is a terribly clunky way of going about doing things with several hotkeys. However, I only just got around to improving the implementation.
If you have use multi-tap hotkeys, I would recommend looking at this function. As a matter of fact, I would recommend it to anyone who uses more than a couple hotkeys, as it can help you crowd a lot more hotkeys into your keyboard layout
MultiTap(CmdListCSV="", Delay=400)
Usage: Chris's multi-tap example is pasted below:
#c:: if winc_presses > 0 ; SetTimer already started, so we log the keypress instead. { winc_presses += 1 return } ; Otherwise, this is the first press of a new series. Set count to 1 and start ; the timer: winc_presses = 1 SetTimer, KeyWinC, 400 ; Wait for more presses within a 400 millisecond window. return KeyWinC: SetTimer, KeyWinC, off if winc_presses = 1 ; The key was pressed once. { Run m:\ ; Open a folder. } else if winc_presses = 2 ; The key was pressed twice. { Run m:\multimedia ; Open a different folder. } else if winc_presses > 2 { MsgBox Three or more clicks detected. } ; Regardless of which action above was triggered, reset the count to ; prepare for the next series of presses: winc_presses = 0 return
To emulate this behavior with MultiTap(), gather each of the result branches into a comma-separated string and pass that string to the first parameter of MultiTap():
#c::MultiTap("Run m:\,Run m:\multimedia,MsgBox Three or more clicks detected.")
https://ahknet.autoh...izontalLine.png
Parameters:[*:2s9oa52a]CmdListCSV: A standard comma-delimited string into which you enter your commands. The first item will be the command executed upon first tap, the second item will be executed upon second tap, etc.
Currently, you have to define each of your commands individually (see the code for more details.) This should be fixed when V2 comes out, as you will be able to dynamically call any command.
If the function does not recognize your command, it will try going to a label, then running a function, then running a file or url, after which it will display an error saying the command could not be interpreted. This allows you to omit the command sometimes.
Optionally, you can also give each command a more friendly label. To do this, precede the command with a label name surrounded with colons like :My Command Name:Run C:\Docs[*:2s9oa52a]Delay: The number of milliseconds to allow between taps.
https://ahknet.autoh...izontalLine.png
Why use MultiTap()?[*:2s9oa52a]Fit multiple related commands into the same hotkey
[*:2s9oa52a]Much more compact than the usual way of doing this; only needs one line
[*:2s9oa52a]Gives feedback with tooltips so you can select the correct option
[*:2s9oa52a]Soon any function will allow dynamic callshttps://ahknet.autohotkey.com/~berban/spacer.pnghttps://ahknet.autohotkey.com/~berban/spacer.pnghttps://ahknet.autohotkey.com/~berban/HorizontalLine.png
Some working examples:
;The following example makes the Windows+E hotkey 5x as useful #e::MultiTap(":My Computer:Run ::{20d04fe0-3aea-1069-a2d8-08002b30309d},:Recycle Bin:Run ::{645ff040-5081-101b-9f08-00aa002f954e},:Program Files:Run " A_ProgramFiles ",:Windows:Run " A_WinDir ",:Start Menu:Run " A_StartMenu)
https://ahknet.autoh...izontalLine.png
Code:
MultiTap(CmdListCSV="", Delay=400, DisplayFunc="") ;http://www.autohotkey.com/forum/viewtopic.php?p=478447 { Static Cache If (CmdListCSV <> "") { StringReplace, TempCSV, CmdListCSV, ", "", All If (@ := InStr(Cache, TempCSV """,")) Cache := SubStr(Cache, 1, @ - 13) ($ := SubStr("00000" A_TickCount + Delay, -9) SubStr("0" (n := SubStr(Cache, @ - 2, 2) + 1), -1) TempCSV) SubStr(Cache, @ + StrLen(TempCSV)) Else Cache .= """" ($ := SubStr("00000" A_TickCount + Delay, -9) "01" TempCSV) """,", n := 1 Loop, Parse, CmdListCSV, CSV If (A_Index = n) { Found := True If RegExMatch(A_LoopField, "^\s*:\K(?:[^:]|::)*(?=:)", Text) StringReplace, Text, Text, ::, :, All Else Text := A_LoopField If (DisplayFunc <> "") and IsFunc(Display) %DisplayFunc%(Text) Else ToolTip, %Text% Break } } If !Found { If (DisplayFunc <> "") and IsFunc(DisplayFunc) %DisplayFunc%() Else ToolTip If (CmdListCSV <> "") StringReplace, Cache, Cache, "%$%"`, n := 0 } If (CmdListCSV = "") { Loop, Parse, Cache, CSV If (A_LoopField <> "") and ((ThisTime > @ := SubStr(A_LoopField, 1, 10)) or (ThisTime = "")) ThisTime := @, ThisNum := SubStr(A_LoopField, 11, 2), ThisCmd := SubStr(A_LoopField, 13) Loop, Parse, ThisCmd, CSV If (A_Index = ThisNum) If (A_LoopField <> "") { RegExMatch(A_LoopField, "s)^\s*(?::(?:[^:]|::)*: ?)?\K(?P<Name>\w*)(?:\((?P<Func>.*?)\)|,? ?(?P<Input>.*))$", Cmd) If (CmdFunc <> "") { ; Will attempt to use a function if you used function notation, i.e. Command(Input1,Input2,Input3) If IsFunc(CmdName) { Loop, Parse, CmdFunc, CSV CmdFunc%A_Index% := A_LoopField %CmdName%(CmdFunc1, CmdFunc2, CmdFunc3, CmdFunc4, CmdFunc5, CmdFunc6) } Else MsgBox, 262160, %A_ScriptName% - %A_ThisFunc%(): Error, Invalid command!`n"%Cmd%" } Else If (CmdName = "Run") Run, %CmdInput%, , UseErrorLevel Else If (CmdName = "Send") Send, %CmdInput% Else If (CmdName = "SendInput") SendInput, %CmdInput% ; If you like, follow the example of the previous 2 lines to add more commands to MultiTap(). %CmdName% will contain the name of the command and %CmdInput% will contain the additional string that is passed as a parameter. Below gives an example of adding message box functionality. ; Else If (CmdName = "MsgBox") ; MsgBox, %CmdInput% Else If (CmdName = "Goto") or (CmdName = "Gosub") SetTimer, %CmdInput%, -1 Else If IsLabel(RegExReplace(Cmd, "\s")) SetTimer, % RegExReplace(Cmd, "\s"), -1 Else If IsFunc(CmdName) %CmdName%(CmdInput) Else { Run, %Cmd%, , UseErrorLevel If ErrorLevel MsgBox, 262160, %A_ScriptName% - %A_ThisFunc%(): Error, Invalid command!`n"%Cmd%" } Break } StringReplace, ThisCmd, ThisCmd, ", "", All StringReplace, Cache, Cache, "%ThisTime%%ThisNum%%ThisCmd%"`, } Loop, Parse, Cache, CSV If (A_LoopField <> "") and ((NextTime > @ := SubStr(A_LoopField, 1, 10)) or (NextTime = "")) NextTime := @ If NextTime SetTimer, MultiTap, % 0 > (@ := A_TickCount - NextTime) ? @ : -1 Return n MultiTap: Return MultiTap() }
https://ahknet.autoh...izontalLine.png
Notes:[*:2s9oa52a]Multiple lines of code are not yet supported for a single action. (An example of this would be Run app, WinWaitActive appwin, WinMaximize) I thought about it but I decided it would probably not be practical anyway. And at any rate it would be best to wait until V2
[*:2s9oa52a]Be careful about using the first optional comma with your commands, as the command lists are comma-separated.
~ Created with Quick Functions for Forums by berban ~