TapHoldManager - Long Press / Multi Tap / Multi Tap and Hold / Any number of Taps / Multi-Keyboard / Joystick buttons
Re: TapHoldManager - Long Press / Multi Tap / Multi Tap and Hold / Any number of Taps / Multi-Keyboard / Joystick button
@Ralf_Reddings200244 a simple please would be nice.
-
- Posts: 94
- Joined: 11 Mar 2023, 14:16
Re: TapHoldManager - Long Press / Multi Tap / Multi Tap and Hold / Any number of Taps / Multi-Keyboard / Joystick button
@Ralf_Reddings200244 oh. It said “pease” & I mustn’t have realised sorry
-
- Posts: 14
- Joined: 18 Jan 2023, 14:18
Re: TapHoldManager - Long Press / Multi Tap / Multi Tap and Hold / Any number of Taps / Multi-Keyboard / Joystick button
Any news on TapHoldManager for AutoHotkey V2?? We really neeed an update on this!!
Re: TapHoldManager - Long Press / Multi Tap / Multi Tap and Hold / Any number of Taps / Multi-Keyboard / Joystick button
What a glorious reply, this helped me getting a better understanding of how THM works which led in putting together a script from scratch, just for THM stuff.SteveMylo wrote: ↑03 Feb 2023, 19:59@Epoch Wow big question.
Firstly it took me months to figure out this stuff
Right, 1st thing you need to do:
During testing I would only use standard keyboard keys & not F13.
that way when everything works you can then use F13 and you will know if F13 is the issue or not.
2nd:
Definitely do not put multiple scripts in a mega script.
I used to do that with THM and it's a very bad idea.
And you are suffering from that now. If you miss just one curly bracket anywhere "{" , it'll come up as an error or it says functions can't be in functions and it WON'T tell you which line the ERROR is in, if it does it is wrong. It's a nightmare.
You will learn faster by having separate scripts for every single key.
3rd:
You have to put all other #include files underneath all the THM functions.
That way FindText function can still work: See below4th:Code: Select all
#Include <TapHoldManager> thm := new TapHoldManager(,,,,"ahk_group MyGroup") thm.Add("z", Func("Z_test"),,,2) #Include <Vector> #Include <FindText> #Include <SoundBeep> #include <UIA_Interface> #Include <ToolTipFM> ; Now start the rest of your script below:
Have a look at my context sensitivity script below:
I made it complex on purpose to cover everything you might need.
If you are using more than one window title, then make it a group.
Here I used the "z" key twice just to show you the power of context sensitivity.
The Double Tap and Hold only works in "myGroup" Windows
and single Tap "z" only works in "ahk_exe chrome.exe"5th:Code: Select all
GroupAdd, MyGroup, ahk_exe Notepad.exe GroupAdd, MyGroup, ahk_exe Evernote.exe #Include <TapHoldManager> thm := new TapHoldManager(,,,,"ahk_group MyGroup") thm.Add("z", Func("Z_test"),,,2) ; ; Douple tap "z" only takes effect in "MyGroup" Active windows thm2 := new TapHoldManager(,,,,"ahk_exe chrome.exe") thm2.Add("z", Func("Z_test2"),,,1) ; Single tap funciton "z" only takes effect in "ahk_exe chrome.exe" Active window ; I put the number '2' in 3 different spots above to make sure the second 'z' funciton/command have different names so there is no conflict, e.g... thm2 / thm2.Add / Z_test2 ; below is where you HAVE TO put the REST of the #include files, BELOW the THM stuff. #Include <HideCursor> #Include <Vector> #Include <FindText> #Include <SoundBeep> #include <UIA_Interface> #Include <ToolTipFM> ; the THM commands can start here: Z_test(isHold, taps, state){ ; Z_test Function is associated with "ahk_group MyGroup" context sensitivity if (isHold=1) & (taps=2) & (state=1){ ; double press and hold soundbeep return } } return Z_test2(isHold, taps, state){ ; Z_test2 Function is associated with "ahk_exe chrome.exe" context sensitivity if (isHold=0) & (taps=1) & (state){ ; single Tap soundbeep soundbeep return } } return ~Esc::ExitApp
As for how the tap and Hold works: It's easy once you know.
* For just TAPS and no HOLD's , then HOLD must always = "0" and (state) should have nothing at all ===> if (isHold=0) & (taps=3) & (state)
* If you are adding a HOLD, then HOLD has to =1 and STATE has to =1 or 0
(state=0) means the hotkey triggers straight away:
And (state=1) means hotkey triggers after releasing the hotkey.
Which I love which is like a Keywait. ===> if (isHold=1) & (taps=3) & (state=1) ; this line means tap 3times but on the 3rd just leave your finger held down and it'll fire after 150ms default
So I notice a mistake in your code in the Space Funciton. state has to be state=0 OR state=1 whenever this is a HOLDCode: Select all
if (isHold=1) & (taps=1) & (state) ; <=== state has to be state=0 OR state=1 { ToolTip One Hold! Sleep 500 ToolTip Return }
6th:
you have to make sure if you don't know already, that the Curley brackets are all there
And have two on the end, otherwise you will get an error saying " you can't have functions inside functions" .Code: Select all
Z_test(isHold, taps, state){ ; Curly bracket here if (isHold=1) & (taps=2) & (state=1){ ; another Curly bracket here soundbeep return } ; this curly bracket closes off the this line ===> if (isHold=1) & (taps=2) & (state=1) ; this is where you add anymore Tap functions like if (isHold=0) & (taps=1) & (state) } ; This last curly bracket closes of the particular Hotkey Funciton. return
I wanted to thank you once again for all your assistance, it's something I don't take for granted and you’ve been one of the most helpful and nice blokes around here. And even if it is supposedly getting better and better in writing code, that’s something you just don’t get with ChatGPT!
I remember trying to get over some issues I’ve encountered on my own, refraining from asking questions as much as I can before I get back to you.
Then, as we all know, life has the habit of getting in the way... So, I feel bad for not doing so earlier, but I wanted to sincerely thank you once more!
Re: TapHoldManager - Long Press / Multi Tap / Multi Tap and Hold / Any number of Taps / Multi-Keyboard / Joystick button
@Epoch Hey no problem, so many people helped me on here too
Re: TapHoldManager - Long Press / Multi Tap / Multi Tap and Hold / Any number of Taps / Multi-Keyboard / Joystick button
Here's v2 conversion:
Code: Select all
#Requires AutoHotkey v2.0
thm := TapHoldManager()
thm.Add("1", MyFunc1)
thm.Add("2", MyFunc2, 250, 500, 2,, "ahk_exe notepad.exe")
MyFunc1(isHold, taps, state){
ToolTip "key: 1`n" (isHold ? "HOLD" : "TAP") "`nTaps: " taps "`nState: " state
}
MyFunc2(isHold, taps, state){
ToolTip "key: 2`n" (isHold ? "HOLD" : "TAP") "`nTaps: " taps "`nState: " state
}
$F1:: ; pause hotkey "2"
{
thm.PauseHotkey("2")
}
$F2:: ; resume hotkey "2"
{
thm.ResumeHotkey("2")
}
$F3:: ; remove hotkey "2"
{
thm.RemoveHotkey("2")
}
Code: Select all
class TapHoldManager {
Bindings := Map(), Bindings.CaseSense := "Off"
__New(tapTime := 150, holdTime := tapTime, maxTaps := -1, prefixes := "$", window := ""){
this.tapTime := tapTime
this.holdTime := holdTime
this.maxTaps := maxTaps
this.prefixes := prefixes
this.window := window
}
Add(keyName, callback, tapTime?, holdTime?, maxTaps?, prefixes?, window?){ ; Add hotkey
if this.Bindings.Has(keyName)
this.RemoveHotkey(keyName)
this.Bindings[keyName] := TapHoldManager.KeyManager(keyName, callback, tapTime ?? this.tapTime, holdTime ?? this.holdTime, maxTaps ?? this.maxTaps, prefixes ?? this.prefixes, window ?? this.window)
}
RemoveHotkey(keyName){ ; to remove hotkey
this.Bindings.Delete(keyName).SetState(0)
}
PauseHotkey(keyName){ ; to pause hotkey temprarily
this.Bindings[keyName].SetState(0)
}
ResumeHotkey(keyName){ ; resume previously deactivated hotkey
this.Bindings[keyName].SetState(1)
}
class KeyManager {
state := 0 ; Current state of the key
sequence := 0 ; Number of taps so far
holdActive := 0 ; A hold was activated and we are waiting for the release
__New(keyName, Callback, tapTime, holdTime, maxTaps, prefixes, window){
this.keyName := keyName
this.Callback := Callback
this.tapTime := tapTime
this.holdTime := holdTime
this.maxTaps := maxTaps
this.prefixes := prefixes
this.window := window
this.HoldWatcherFn := this.HoldWatcher.Bind(this)
this.TapWatcherFn := this.TapWatcher.Bind(this)
this.JoyWatcherFn := this.JoyButtonWatcher.Bind(this)
this.DeclareHotkeys()
}
DeclareHotkeys(){
if (this.window)
HotIfWinactive this.window ; sets the hotkey window context if window option is passed-in
Hotkey this.prefixes this.keyName, this.KeyEvent.Bind(this, 1), "On" ; On option is important in case hotkey previously defined and turned off.
if (this.keyName ~= "i)^\d*Joy"){
Hotkey this.keyName " up", (*) => SetTimer(this.JoyWatcherFn, 10), "On"
} else {
Hotkey this.prefixes this.keyName " up", this.KeyEvent.Bind(this, 0), "On"
}
if (this.window)
HotIfWinactive ; restores hotkey window context to default
}
SetState(state){ ; turns On/Off hotkeys (should be previously declared) // state is either "1: On" or "0: Off"
; "state" under this method context refers to whether the hotkey will be turned on or off, while in other methods context "state" refers to the current activity on the hotkey (whether it's pressed or released (after a tap or hold))
if (this.window)
HotIfWinactive this.window
state := (state ? "On" : "Off")
Hotkey this.prefixes this.keyName, state
if (this.keyName ~= "i)^\d*Joy"){
Hotkey this.keyName " up", state
} else {
Hotkey this.prefixes this.keyName " up", state
}
if (this.window)
HotIfWinactive
}
JoyButtonWatcher(){
if GetKeyState(this.keyName)
return
SetTimer this.JoyWatcherFn, 0
this.KeyEvent(0)
}
KeyEvent(state, *){
if (state == this.state)
return ; Suppress Repeats
this.state := state
if (state){
; Key went down
this.sequence++
SetTimer this.HoldWatcherFn, -this.holdTime
} else {
; Key went up
SetTimer this.holdWatcherFn, 0
if (this.holdActive){
this.holdActive := 0
SetTimer this.FireCallback.Bind(this, this.sequence, 0), -1
this.sequence := 0
return
}
if (this.maxTaps > 0 && this.Sequence == this.maxTaps){
SetTimer this.tapWatcherFn, 0
SetTimer this.FireCallback.Bind(this, this.sequence, -1), -1
this.sequence := 0
} else {
SetTimer this.tapWatcherFn, -this.tapTime
}
}
}
; If this function fires, a key was held for longer than the tap timeout, so engage hold mode
HoldWatcher(){
if (this.sequence > 0 && this.state == 1){
; Got to end of tapTime after first press, and still held.
; HOLD PRESS
SetTimer this.FireCallback.Bind(this, this.sequence, 1), -1
this.holdActive := 1
}
}
; If this function fires, a key was released and we got to the end of the tap timeout, but no press was seen
TapWatcher(){
if (this.sequence > 0 && this.state == 0){
; TAP
SetTimer this.FireCallback.Bind(this, this.sequence), -1
this.sequence := 0
}
}
FireCallback(seq, state := -1){
this.Callback.Call(state != -1, seq, state)
}
}
}
-
- Posts: 94
- Joined: 11 Mar 2023, 14:16
Re: TapHoldManager - Long Press / Multi Tap / Multi Tap and Hold / Any number of Taps / Multi-Keyboard / Joystick button
@ntepa
Wow, I was not expecting this at all. Seems to be working fine too!
Thank you so much man! Unbalievable.
By the way you should consider releasing this on the V2 section! This is a much needed port
Wow, I was not expecting this at all. Seems to be working fine too!
Thank you so much man! Unbalievable.
By the way you should consider releasing this on the V2 section! This is a much needed port
Re: TapHoldManager - Long Press / Multi Tap / Multi Tap and Hold / Any number of Taps / Multi-Keyboard / Joystick button
Yeah and make a repository on Github. Thanks for this.
-
- Posts: 14
- Joined: 18 Jan 2023, 14:18
Re: TapHoldManager - Long Press / Multi Tap / Multi Tap and Hold / Any number of Taps / Multi-Keyboard / Joystick button
What about porting the InterceptionTapHold.ahk file to v2?
Re: TapHoldManager - Long Press / Multi Tap / Multi Tap and Hold / Any number of Taps / Multi-Keyboard / Joystick button
Update: I am currently working on a release of THM for AHK v2
A WIP version can be found here
It should be identical in usage (Apart from obviously not having to wrap your function names in Func()), except for one minor change.
In v1, if you wanted to skip an optional parameter, you used -1 (eg thm.Add("1" , Func("MyFunc1"), -1, -1 , 2).
In v2, you just omit the parameter (eg thm.Add("1" , MyFunc1 ,,, 2))
I could have kept using -1, but with v2's support for unset parameters and coalescing operators, it just felt a lot cleaner (And consistent with AHK's normal syntax) to make this change - thanks to @ntepa for the inspiration on this one, and for doing an interim version.
@milliard - this version includes support for AHI
A WIP version can be found here
It should be identical in usage (Apart from obviously not having to wrap your function names in Func()), except for one minor change.
In v1, if you wanted to skip an optional parameter, you used -1 (eg thm.Add("1" , Func("MyFunc1"), -1, -1 , 2).
In v2, you just omit the parameter (eg thm.Add("1" , MyFunc1 ,,, 2))
I could have kept using -1, but with v2's support for unset parameters and coalescing operators, it just felt a lot cleaner (And consistent with AHK's normal syntax) to make this change - thanks to @ntepa for the inspiration on this one, and for doing an interim version.
@milliard - this version includes support for AHI
-
- Posts: 94
- Joined: 11 Mar 2023, 14:16
Re: TapHoldManager - Long Press / Multi Tap / Multi Tap and Hold / Any number of Taps / Multi-Keyboard / Joystick button
@evilC
This is great news! I am glad you have plans to continue with this project and adopt AutoHotkey V2. Made my week! Looking forward to it
This is great news! I am glad you have plans to continue with this project and adopt AutoHotkey V2. Made my week! Looking forward to it
-
- Posts: 74
- Joined: 07 Mar 2023, 05:20
Re: TapHoldManager - Long Press / Multi Tap / Multi Tap and Hold / Any number of Taps / Multi-Keyboard / Joystick button
Wooohoo! great news evilC. There can be no talk of AutoHotkeyV2 without Tap Hold Manager. Cant wait for it.