TapHoldManager - Long Press / Multi Tap / Multi Tap and Hold / Any number of Taps / Multi-Keyboard / Joystick buttons

Post your working scripts, libraries and tools for AHK v1.1 and older
User avatar
evilC
Posts: 4822
Joined: 27 Feb 2014, 12:30

TapHoldManager - Long Press / Multi Tap / Multi Tap and Hold / Any number of Taps / Multi-Keyboard / Joystick buttons

04 Mar 2018, 11:31

This is a handy class library that you can use to double up functionality on keys with long-press and double-tap type functionality.

You create one instance of the class per key you wish to monitor, and it fires one of your functions (not labels! functions only!) when tap or hold events happen

There is a "tapTime" timeout for each hotkey - this defaults to 200ms
When you press a key, if you release it again within 200ms, it is considered a "Tap".
However, more taps could follow, so if the key is pressed again within 200ms, then the tap count increases.
The cycle then repeats, as long as there is no more than 200ms between each tap and release
Once there is no other tap within 200ms, then the tap count stops and your function is fired and passed the number of taps that occurred.

When you press a key (on either the first tap or on a press after a previous tap) and keep it held for 200ms, then that is considered a "Hold".
As soon as this occurs, your hold function is called, and passed the number of taps that happened before the hold, and 1 to signify button down.
Once the key is released, your function is fired again, with 0 to signify up.

Github Page (Documentation here)

Releases Page (Download from here)

Note that TapHoldManager has a variant library that makes it work with AutoHotInterception, which would allow you to respond to Taps and Holds only on a specific keyboard, whilst not affecting the main keyboard. This has been tested, for example, with an Xbox controller keyboard.
Last edited by evilC on 09 Oct 2018, 13:55, edited 8 times in total.
User avatar
evilC
Posts: 4822
Joined: 27 Feb 2014, 12:30

Re: TapAndHoldManager - Long Press / Multi Tap / Multi Tap and Hold, Any number of Taps

04 Mar 2018, 16:58

Examples

Simple Example
Declare hotkeys for the 1 and 2 keys, and route each to it's own function

Code: Select all

; If the library files are in a subfolder called Lib next to the script, use this
#include Lib\TapHoldManager.ahk
; If you placed all the library files in your My Documents\AutoHotkey\lib folder, use this
;#include <TapHoldManager>

#SingleInstance force

thm := new TapHoldManager()	; TapTime / Prefix can now be set here
thm.Add("1", Func("MyFunc1"))
thm.Add("2", Func("MyFunc2"))

MyFunc1(isHold, taps, state){
	ToolTip % "1`n" (isHold ? "HOLD" : "TAP") "`nTaps: " taps "`nState: " state
}

MyFunc2(isHold, taps, state){
	ToolTip % "2`n" (isHold ? "HOLD" : "TAP") "`nTaps: " taps "`nState: " state
}

Turning Hotkeys On and Off
Before you declare the hotkey, execute Hotkey, If, <your condition>, where <your condition> is code that you would put in an #if statement.
Then, in your code, define an empty #if block that matches <your condintion>
For example:

Code: Select all

Hotkey, If, toggle == 1
f1Remapper := new TapAndHoldManager("F1", Func("F1TapFunc"))
f2Remapper := new TapAndHoldManager("F2", Func("F2TapFunc"))
return

; Empty #if block for the hotkey, if statement to match
#if toggle == 1
#if
[...]
Multiple Hotkeys (Bound Function Method)
It is also worth noting that you do not have to have one set of functions per hotkey, you can route everything through one set of functions using "bound functions"
Here is the simple example done with bound functions:

Code: Select all

#include TapAndHoldManager.ahk
#SingleInstance force

f1Remapper := new TapAndHoldManager("F1", Func("TapFunc").Bind("F1"))
f2Remapper := new TapAndHoldManager("F2", Func("TapFunc").Bind("F2"))

TapFunc(key, isHold, taps, state){
	ToolTip % key "`n" (isHold ? "HOLD" : "TAP") "`nTaps: " taps "`nState: " state
}

^Esc::ExitApp

Last edited by evilC on 23 Mar 2018, 10:32, edited 3 times in total.
User avatar
evilC
Posts: 4822
Joined: 27 Feb 2014, 12:30

Re: TapAndHoldManager - Long Press / Multi Tap / Multi Tap and Hold, Any number of Taps

04 Mar 2018, 17:01

Complete, Useful Scripts

Mapping keyboard to mouse, with acceleration:
Double-tap and hold to move quicker

Code: Select all

/*
Remaps Keyboard Numpad to Mouse cursor movement
Press and hold a key to move
Double-tap and hold to move quicker
Triple-tap and hold to move even quicker
etc...
*/
#include Lib\TapHoldManager.ahk
MoveMultiplier := 5
HoldMoveVectors := {x: 0, y: 0}
MoveFn := Func("DoMove")

thm := new TapHoldManager()
thm.Add("NumpadLeft", Func("HandleInput").Bind("x", "-1"))
thm.Add("NumpadRight", Func("HandleInput").Bind("x", "1"))

thm.Add("NumpadUp", Func("HandleInput").Bind("y", "-1"))
thm.Add("NumpadDown", Func("HandleInput").Bind("y", "1"))

HandleInput(axis, dir, isHold, taps, state := -1){
	global HoldMoveVectors, MoveFn, MoveMultiplier
	ToolTip % "isHold: " isHold ", Axis: " axis ", Dir: " dir ", Taps: " taps ", State: " state
	if (state == 1){
		HoldMoveVectors[axis] := (dir * taps) * MoveMultiplier
		SetTimer, % MoveFn, 10
	} else if (state == 0){
		HoldMoveVectors[axis] := 0
		if (HoldMoveVectors.x == 0 && HoldMoveVectors.y == 0){
			SetTimer, % MoveFn, Off
		}
	} else {
		; Tap
		x := 0
		y := 0
		%axis% := (dir * taps) * MoveMultiplier * MoveMultiplier
		MouseMove, % x, % y, 0, R
	}
}

DoMove(){
	global HoldMoveVectors
	MouseMove, % HoldMoveVectors.x, % HoldMoveVectors.y , 0, R
}
^Esc::ExitApp
Last edited by evilC on 23 Mar 2018, 10:40, edited 1 time in total.
User avatar
evilC
Posts: 4822
Joined: 27 Feb 2014, 12:30

Re: TapHoldManager - Long Press / Multi Tap / Multi Tap and Hold, Any number of Taps

23 Mar 2018, 10:22

TapAndHoldManager is now TapHoldManager and is hosted on GitHub

New stuff added:
Ability to configure the holdTime (The time you have to hold a key to trigger a hold) - it defaults to the same as tapTime
Optional parameters can now be set at the manager level, and overridden for individual keys
An add-on is included to make TapHoldManager work with AutoHotInterception, meaning you can respond to Taps and Holds on other keyboards, whilst not affecting your main keyboard.
There is now one callback function, which is passed an isHold parameter, to indicate if the callback is for a tap or a hold
User avatar
Thoughtfu1Tux
Posts: 125
Joined: 31 May 2018, 23:26

Re: TapHoldManager - Long Press / Multi Tap / Multi Tap and Hold / Any number of Taps / Multi-Keyboard / Joystick button

09 Oct 2018, 23:57

This is fantastic, thank you!

I have been using the example function that is listed within the SetTimer documentation to do run different actual depending on the number of times the hotkey is pressed within a 500ms duration, this will be a great replacement that will be able to do much more than what I have right now.
User avatar
evilC
Posts: 4822
Joined: 27 Feb 2014, 12:30

Re: TapHoldManager - Long Press / Multi Tap / Multi Tap and Hold / Any number of Taps / Multi-Keyboard / Joystick button

28 Feb 2019, 10:24

THM v1.4 has been released.

Added MaxTaps parameter to support always firing callback after a fixed number of taps / holds
Use a MaxTaps of 1 if you just want tap / long-press support and don't want multi-tap.
This will force the callback to fire after every key press, so eg 3 quick taps will fire the callback 3 times with a taps value of 1, rather than firing it once with a taps value of 3.

PLEASE NOTE THAT THE MAXTAPS PARAMETER WAS NOT ADDED AT THE END!
It was added before the "Prefix" parameter, so if you used that, you would have to shift your parameters along by 1
See the Documentation
rj8810
Posts: 31
Joined: 16 Jul 2018, 22:34

Re: TapHoldManager - Long Press / Multi Tap / Multi Tap and Hold / Any number of Taps / Multi-Keyboard / Joystick button

08 Jun 2019, 00:55

HELLO...
THIS IS WONDERFUL BUT,
HELP PLEASE, I DO NOT KNOW HOW TO USE THIS SRCIPT, I DO NOT ACHIEVE UNDERSTAND ITS CODE

HOW CAN I DO THIS?
4:: ; THIS IS MY HOTKEY

4 SIMPLE::
MSGBOX ACTION 1
4 DOUBLE::
MSGBOX ACTION 2
4 TRIPLE::
MSGBOX ACTION 3
4 LONG PRESS1TIME ::
MSGBOX ACTION 4
4 LONG PRESS TWICE ::
MSGBOX ACTION 5
4 SIMPLE+LONGPRESS::
MSGBOX ACTION 6
4 LONGPRESS+SIMPLE::
MSGBOX ACTION 7
4SIMPLE+LONGPRESS TWICE ::
MSGBOX ACTION 8
4LONGPRESS TWICE +SIMPLE::
MSGBOX ACTION 9
4LONGPRESS+SIMPLE TWICE ::
MSGBOX ACTION 10
4SIMPLE TWICE +LONGPRES::
MSGBOX ACTION 11


AND LIKE HOW CODE MORSE
STOP= WAIT 500 MS

4SIMPLE+STOP+SIMPLE ; THIS IS DIFERENT A PRESS DOBLE OR PRESS TWICE CONTINUOUS. MORSE IS ./.
MSGBOX ACTION 12
4SIMPLE+STOP+SIMPLE TWICE ; MORSE IS ./..
MSGBOX ACTION 13
4SIMPLE TWICE+STOP+SIMPLE ; MORSE IS ../.
MSGBOX ACTION 14

THANK YOU IN ADVANCE FOR ANY HELP
User avatar
evilC
Posts: 4822
Joined: 27 Feb 2014, 12:30

Re: TapHoldManager - Long Press / Multi Tap / Multi Tap and Hold / Any number of Taps / Multi-Keyboard / Joystick button

17 Jun 2019, 07:15

THM on it's own will not detect two long presses followed by a short press, you would have to code that yourself.
Here is how to detect multi-tap and mult-tap followed by long-press

Code: Select all

#include Lib\TapHoldManager.ahk
#SingleInstance force

thm := new TapHoldManager()
thm.Add("4", Func("MyFunc"))

MyFunc(isHold, taps, state){
	if (!state)
		return ; Only take action on key press, not key release
	if (isHold){
		; Holds
		if (taps == 1){
			msgbox ACTION 5
		} else if (taps == 2){
			msgbox ACTION 6
		}
	} else {
		; Taps
		if (taps == 1){
			msgbox ACTION 1
		} else if (taps == 2){
			msgbox ACTION 2
		}
	}
}
User avatar
evilC
Posts: 4822
Joined: 27 Feb 2014, 12:30

Re: TapHoldManager - Long Press / Multi Tap / Multi Tap and Hold / Any number of Taps / Multi-Keyboard / Joystick button

08 Jul 2019, 11:19

I have updated the documentation on the GitHub site to attempt to clarify what the various parameters (tapTime and holdTime) do, and how they affect the logic.
I also added some diagrams to try and make it as clear as possible. Feedback welcomed!
Phazor
Posts: 10
Joined: 28 Jul 2019, 18:15

Re: TapHoldManager - Long Press / Multi Tap / Multi Tap and Hold / Any number of Taps / Multi-Keyboard / Joystick button

29 Jul 2019, 13:50

I know this is an old thread, but I'm a super newbie to AHK and I can't seem to figure out how to do this with the surface pen. The only way I can adjust the surface pen in AHK is by using another software to remap the side buttons to Right Click and Middle Mouse Button, but even then the right click and middle mouse button is not working with TapHoldManager… I'm trying tochange the surface pen's side buttons to have Tap Middle Mouse Button to toggle Ctrl, and Double Tap to toggle Shift, and Press and Hold to hold Middle Mouse down until release, and Double Tap and hold to Hold Middle Mouse and Ctrl until I release the button. I'm also trying to have multiple functions for the Right Click buttons for hover click, double tap for Alt toggle, etc.

Here's my code

Code: Select all

#include Lib\TapHoldManager.ahk
#SingleInstance force

thm := new TapHoldManager()
thm.Add("MButton", Func("MyFunc"))

MyFunc(isHold, taps, state){
	if (!state)
		return ; Only take action on key press, not key release
		if (isHold){
			; Holds
				if (taps == 1){
				Send {MButton}
				} 
				else if (taps == 2){
				Send {^MButton}
		}
	}
	else {
		; Taps
		if (taps == 1){
			send % "{Ctrl " ((s:=!s) ? "up" : "down") "}"
		} else if (taps == 2){
			send % "{Shift " ((s:=!s) ? "up" : "down") "}"
		}
	}
}

None of it works... What am I doing wrong? Please speak in newbie language :) thank you. Oh, and I forgot to mention I'm trying to figure out how to change the (taps == 2) to Ctrl Middle Click (hold until release) but it doesn't work.
User avatar
evilC
Posts: 4822
Joined: 27 Feb 2014, 12:30

Re: TapHoldManager - Long Press / Multi Tap / Multi Tap and Hold / Any number of Taps / Multi-Keyboard / Joystick button

30 Jul 2019, 10:46

if (!state) is probably your problem.
On a hold, state will be 1 when the hold triggers, then 0 when you release the key

For a tap, state is always -1, which is logically true, so your Taps code will never trigger on a tap, but will trigger on a hold

Your code structure should be more like this:

Code: Select all

MyFunc(isHold, taps, state){
	if (state == -1)
		; Taps - triggers on release of key
		if (taps == 1){
			send % "{Ctrl " ((s:=!s) ? "up" : "down") "}"
		} else if (taps == 2){
			send % "{Shift " ((s:=!s) ? "up" : "down") "}"
		}
	}
	else {
		; Holds
		if (state){
			; Handle beginning (press) of hold here (Optional)
			if (taps == 1){
				Send {MButton}
			} 
			else if (taps == 2){
				Send {^MButton}
			}
		} else {
			; Handle end (release) of hold here (Optional)
		}
	}
}
When you say "by using another software to remap the side buttons to Right Click and Middle Mouse Button", what software do you mean? If the surface pen comes with software that lets you assign mouse or keyboard actions to the pen buttons, then it's entirely possible that you will be able to use AutoHotInterception to respond to input coming from the surface pen and only the surface pen. As it stands, even if you did get the above code working, it would also respond to input coming from your normal keyboard and mouse - so if eg you used THM to make a long-press action for left mouse, that would stop click and drag working on your normal mouse.
So my recommendation would be to try installing AutoHotInterception, run the demo monitor app, and see if it sees key / mouse input coming from the surface pen
Phazor
Posts: 10
Joined: 28 Jul 2019, 18:15

Re: TapHoldManager - Long Press / Multi Tap / Multi Tap and Hold / Any number of Taps / Multi-Keyboard / Joystick button

30 Jul 2019, 14:26

When you say 'by using another software to remap the side buttons to Right Click and Middle Mouse Button' , what software do you mean? If the surface pen comes with software that lets you assign mouse or keyboard actions to the pen buttons, then it's entirely possible that you will be able to use AutoHotInterception to respond to input coming from the surface pen and only the surface pen.
I'm using a software called Radial Menu to remap the buttons. The software doesn't come with the surface Pen but it was designed for the pen for a 3rd party.
As it stands, even if you did get the above code working, it would also respond to input coming from your normal keyboard and mouse - so if eg you used THM to make a long-press action for left mouse, that would stop click and drag working on your normal mouse.
So my recommendation would be to try installing AutoHotInterception, run the demo monitor app, and see if it sees key / mouse input coming from the surface pen
I downloaded AHI but it says that I need to copy a dll file into the lib for it to work and there is no such dll in the download idk. Aside from that, the code doesn't seem to be working. After the 1st If statement and after the nested If/if-else statement, there was an error saying the else argument had no supporting if. I deleted one of the curly brackets before the else argument and the code ran, but didn't do anything. I wasn't able to use left click or the pen at all, but I do appreciate your help in explaining the difference between hold and tap (1,0,-1) and helping with the code.
Phazor
Posts: 10
Joined: 28 Jul 2019, 18:15

Re: TapHoldManager - Long Press / Multi Tap / Multi Tap and Hold / Any number of Taps / Multi-Keyboard / Joystick button

31 Jul 2019, 00:21

Currently, no action triggers while the code is running, neither when I tap, nor hold. The Mbutton does nothing. Any Ideas?
r4961
Posts: 3
Joined: 20 Oct 2019, 11:36

Re: TapHoldManager - Long Press / Multi Tap / Multi Tap and Hold / Any number of Taps / Multi-Keyboard / Joystick button

23 Oct 2019, 11:00

Nice to meet you, EvilC!!

I have researched this forum for days, and while I have enough AHK coding knowledge for simple solutions, I have nowhere near what you purportedly have put together regarding multi-tap and and specific-device rejection/acceptations.


You can increase the usability of these 2 AHK scripts you've created exponentially IMO with a common solution to writing on and using OneNote....not to mention other writing apps.

RadialMenu is cool, but is old and only gets you so far with automation, because there are 3 buttons available to remap, but not only do all styluses not have all 3 (1 of my 2 stylus buttons only responds to button 3), but they are all automated on a single tap. When you are with a stylus, u usually NEED your single taps to stay as what they're meant for.



For my particular stylus current status, here is the usability issue:

1) It's a Surface Pen with 2 buttons. One that is a right mouse click, and the other, a Left Click button outside of ONENOTE, that for now, only has ERASE functionality once it's by the screen WITHIN ONENOTE.

Long story short, not only would multi taps with RButton and this taken-over LButton increase the usability of someone holding the tablet and doing work (Screenshots, Inserts, Tables, et. al.),.... but as of right now,...you cannot click and drag something with the stylus as you normally would with LButton everywhere else, because it will ONLY convert to eraser mode. As of now, when you go to resize things, you're forced to pretty much use your finger to activate. With the stylus are not allowed to do the usual clicking, dragging, and opening you do with the traditional LButton. Once you use your mouse cursor, with LButton, you are able to, so long as the pen tip is not close to the screen!

I have tried the same thing with Samsung's inking technology (different than Surface's),...and I get the same result.

Accomplishing this and other things with 2 taps or more of stylus buttons would be PHENOMENAL, and a breakthrough for all inking apps, and in particular, Microsoft (OneNote) that they would probably owe you for. Hope you are in on this and have some ideas.
Attachments
best-tablets-for-onenote.jpg
best-tablets-for-onenote.jpg (11.33 KiB) Viewed 15225 times
omareg94
Posts: 94
Joined: 27 Jun 2016, 22:46

Re: TapHoldManager - Long Press / Multi Tap / Multi Tap and Hold / Any number of Taps / Multi-Keyboard / Joystick button

16 Apr 2020, 10:46

Hello evilC,

Is it possible to declare a hotkey limited to specific window in order not to conflict with other hotkeys already declared for other windows?

Thank you.

Update:
I've solved it. Kindly check this.
@evilC
Avastgard
Posts: 130
Joined: 30 Sep 2016, 21:54

Re: TapHoldManager - Long Press / Multi Tap / Multi Tap and Hold / Any number of Taps / Multi-Keyboard / Joystick button

22 Jun 2020, 06:37

How does TapHoldManager compare to RapidHotkey (https://autohotkey.com/board/topic/35566-rapidhotkey/page-1). I'm trying to decide which one to use, but being a beginner, I'm not sure I fully understand the scope of both scripts.

Return to “Scripts and Functions (v1)”

Who is online

Users browsing this forum: No registered users and 82 guests