{"id":13445776,"url":"https://github.com/evilC/TapHoldManager","last_synced_at":"2025-03-21T05:31:08.350Z","repository":{"id":40462141,"uuid":"124130725","full_name":"evilC/TapHoldManager","owner":"evilC","description":"An AHK library for Long Press / Multi tap / Multi tap and hold","archived":false,"fork":false,"pushed_at":"2024-07-29T15:02:43.000Z","size":235,"stargazers_count":147,"open_issues_count":8,"forks_count":13,"subscribers_count":10,"default_branch":"master","last_synced_at":"2025-02-04T13:44:37.396Z","etag":null,"topics":[],"latest_commit_sha":null,"homepage":null,"language":"AutoHotkey","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/evilC.png","metadata":{"files":{"readme":"README.md","changelog":"CHANGELOG.md","contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null}},"created_at":"2018-03-06T19:54:17.000Z","updated_at":"2025-01-27T10:30:00.000Z","dependencies_parsed_at":"2024-04-09T18:30:52.350Z","dependency_job_id":"ab572fc4-e32d-4225-8fd1-73536e60e130","html_url":"https://github.com/evilC/TapHoldManager","commit_stats":null,"previous_names":[],"tags_count":7,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/evilC%2FTapHoldManager","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/evilC%2FTapHoldManager/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/evilC%2FTapHoldManager/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/evilC%2FTapHoldManager/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/evilC","download_url":"https://codeload.github.com/evilC/TapHoldManager/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":244745702,"owners_count":20503045,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":[],"created_at":"2024-07-31T05:00:39.376Z","updated_at":"2025-03-21T05:31:07.906Z","avatar_url":"https://github.com/evilC.png","language":"AutoHotkey","funding_links":[],"categories":["AutoHotkey"],"sub_categories":[],"readme":"# TapHoldManager\n\nAn AutoHotkey library that allows you to map multiple actions to one key using a tap-and hold system  \nLong press / Tap / Multi Tap / Tap and Hold / Multi Tap and Hold etc are all supported  \n\n# Getting Help\n\nUse the [TapHoldManager Discussion Thread on the AHK Forums](https://autohotkey.com/boards/viewtopic.php?f=6\u0026t=45116)\n\nOr join the [HidWizards Discord server](https://discord.gg/hjeZvkyGxB)\n\n# Normal Usage\n\n## Setup\n\n1. Download a zip from the releases page\n2. Extract the zip to a folder of your choice\n3. Run the Example script\n\n## Usage\n\nInclude the library\n\n```autohotkey\n#include Lib\\TapHoldManager.ahk\n```\n\nInstantiate TapHoldManager\n\n```autohotkey\n;v1\nthm := new TapHoldManager()\n\n;v2\nthm := TapHoldManager()\n```\n\nAdd some key(s)\n\n```autohotkey\n;v1\nthm.Add(\"1\", Func(\"MyFunc1\"))\nthm.Add(\"2\", Func(\"MyFunc2\"))\n\n;v2\nthm.Add(\"1\", MyFunc1)\nthm.Add(\"2\", MyFunc2)\n```\n\nAdd your callback function(s)\n\n```autohotkey\n;v1\nMyFunc1(isHold, taps, state){\n    ToolTip % \"1`n\" (isHold ? \"HOLD\" : \"TAP\") \"`nTaps: \" taps \"`nState: \" state\n}\n\nMyFunc2(isHold, taps, state){\n    ToolTip % \"2`n\" (isHold ? \"HOLD\" : \"TAP\") \"`nTaps: \" taps \"`nState: \" state\n}\n\n\n; v2\nMyFunc1(isHold, taps, state){\n    ToolTip(\"1`n\" (isHold ? \"HOLD\" : \"TAP\") \"`nTaps: \" taps \"`nState: \" state)\n}\n\nMyFunc2(isHold, taps, state){\n    ToolTip(\"2`n\" (isHold ? \"HOLD\" : \"TAP\") \"`nTaps: \" taps \"`nState: \" state)\n}\n```\n\n`IsHold` will be true if the event was a hold. If so, `state` will holl `1` for pressed or `0` for released.  \nIf `IsHold` is false, the event was a tap, and `state` will be `-1`  \nIn either case, `taps` holds the number of taps which occurred.  \nFor example, if I double-tap, `IsHold` will be false, and `taps` will be `2`.  \nIf I double-tapped and held on the second tap, then on press the function would be fired once with `IsHold` as true, taps would as `2` and state as `1`. When the key is released, the same but `state` would be `0`  \n\n## Instantiating TapHoldManager\n\n```autohotkey\n;v1\nthm := new TapHoldManager([\u003ctapTime\u003e, \u003choldTime\u003e, \u003cmaxTaps\u003e, \u003cprefix\u003e, \u003cwindow\u003e])\n\n;v2\nthm := TapHoldManager([\u003ctapTime\u003e, \u003choldTime\u003e, \u003cmaxTaps\u003e, \u003cprefix\u003e, \u003cwindow\u003e])\n```\n\nThat is to say, all parameters are optional. If omitted, they will use the default value (See below).\n\n**tapTime** The amount of time after a tap occured to wait for another tap.  \nDefaults to 150ms.  \n**holdTime** The amount of time that you need to hold a button for it to be considered a hold.  \nDefaults to the same as `tapTime`.  \n**maxTaps** The maximum number of taps before the callback will be fired.  \nDefaults to infinite.  \nSetting this value to `1` will force the callback to be fired after every tap, whereas by default if you tapped 3 times quickly it would fire the callback once and pass it a `taps` value of `3`, it would now be fired 3 times with a `taps` value of `1`.  \nIf `maxTaps` is 1, then the `tapTime` setting will have no effect.  \n**prefix** The prefix used for all hotkeys, default is `$`  \n**window** An AHK [WinTitle](https://www.autohotkey.com/docs/misc/WinTitle.htm) string that defines which windows the hotkey will take effect in  \nFor example, to make Hotkeys only work in Notepad, you could use:  \n`thm := new TapHoldManager(,,,,\"ahk_exe notepad.exe\")`  \n\nYou can pass as many parameters as you want.  \n`thm := new TapHoldManager()`  \n`thm := new TapHoldManager(100, 200, 1, \"$*\")`  \n\nWhen specifying parameters, you can omit the parameter to leave that parameter at it's default.  \nFor example, if you only wish to alter the `prefix` (3rd) parameter, you could do:  \n`thm := new TapHoldManager(,,, \"$*\")` \nOr  \n`thm := new TapHoldManager(,,, \"$*\")` \n\n## Adding Hotkeys\n\n`thm.Add(\u003ckeyname\u003e, \u003ccallback (function object)\u003e, [tapTime, holdTime, maxTaps, prefix, window])`  \nWhen adding keys, you can also optionally add *tapTime*, *holdTime*, *maxTaps*, *prefix*, or *window* parameters (As used when instantiating the *TapHoldManager* class) to the end to override the TapHoldManager's settings . For example:\n\n```autohotkey\n;v1\nthm := new TapHoldManager(,200)        ; override holdTime to 200\nthm.Add(\"1\", Func(\"MyFunc1\"), 100)     ; override tapTime to 100\nthm.Add(\"2\", Func(\"MyFunc2\"), , ,4)    ; override maxTaps to 4\n\n; v2\nthm := TapHoldManager(,200)            ; override holdTime to 200\nthm.Add(\"1\", MyFunc1, 100)             ; override tapTime to 100\nthm.Add(\"2\", MyFunc2, , ,4)            ; override maxTaps to 4\n```\n\nIn the above example, the TapHoldManager class (`thm`) uses all default settings except `holdTime`, which is changed to `200`.\n\nKey `1` uses a `tapTime` of  `100`, and uses the modified `holdTime` of `200` which was inherited from `thm` - all other options are default.\n\nKey `2` uses a  `holdTime` of `200` (Inherited from `thm`) and a `maxTaps` of `4` - all other options are default\n\n## Enabling or Disabling Hotkeys\n\nHotkeys can be disabled by calling `PauseHotkey` and passing the name of the key to pause:  \n`thm.PauseHotkey(\"1\")`  \n\nHotkeys can be re-enabled by calling `ResumeHotkey` and passing the name of the key to resume:  \n`thm.PauseHotkey(\"1\")`  \n\nHotkeys can be removed by calling `RemoveHotkey` and passing the name of the key to remove:  \n`thm.RemoveKey(\"1\")`  \n\n## Logic Flowchart\n\n(Note that the firing of the function on key release during a hold is omitted from this diagram for brevity)\n![flowchart](LogicFlowchart.png)  \n\n## Example Timelines\n\n![timeline](Timelines.png)\n\n## Minimizing response times\n\nTo make taps fire as quickly as possible, set `tapTime` as low as possible.  \nIf you do not require multi-tap or multi-tap-and-hold, then setting `maxTaps` to 1 will make taps respond instantly upon release of the key. In this mode, the `tapTime` setting will have no effect.  \nTo make holds fire as quickly as possible, setting `holdTime` as low as possible will help. `maxTaps` will not affect response time of holds, as they always end a sequence.\n\n# Integration with the Interception driver (Multiple Keyboard support)\n\nTapHoldManager can use the [Interception driver](http://www.oblita.com/interception) to add support for per-keyboard hotkeys - you can bind TapHoldManager to keys on a second keyboard, and use them completely independently of your main keyboard.  \n\n## Interception Setup\n\n1. Set up my [AutoHotInterception](https://github.com/evilC/AutoHotInterception) AHK wrapper for Interception.  \n   Get the interception example running, so you know AHK can speak to interception OK.  \n2. Download the latest release of TapHoldManager from the releases page and extract it to a folder of your choice.  \n3. You need to add the files from TapHoldManager's lib folder to AutoHotInterception's lib folder.  \n   (Or, you can make sure the contents of both lib folders are in `My Documents\\AutoHotkey\\Lib`)  \n4. Enter the VID / PID of your keyboard(s) into the Interception example script and run  \n\n## AutoHotInterception modes\n\nTapHoldManager works in both AutoHotInterception modes - \"Context\" and \"Subscription\" modes.  \n\n### AutoHotInterception Context Mode\n\nJust before you call `Add`, set the context for hotkeys using `hotkey, if, \u003ccontext var\u003e` which matches that which your AHK Context Callback sets.  \nFor example:  \n\n```autohotkey\n#include Lib\\TapHoldManager.ahk\n#include \u003cAutoHotInterception\u003e\n\nAHI := new AutoHotInterception()\nkeyboardId := AHI.GetKeyboardId(0x03EB, 0xFF02)\ncm1 := AHI.CreateContextManager(keyboardId)\n\nthm := new TapHoldManager()\n\nhotkey, if, cm1.IsActive\nthm.Add(\"1\", Func(\"MyFunc1\"))\nhotkey, if\nreturn\n\nMyFunc1(isHold, taps, state){\n    Tooltip % \"IsHold: \" isHold \"`nTaps: \" taps \"`nState: \" state\n}\n\n#if cm1.IsActive ; Hotkey, if needs a context to match, even if it is empty\n#if\n```\n\n### AutoHotInterception Subscription Mode\n\nA wrapper is included which extends the TapHoldManager class and replaces the hotkey bind code with Interception bind code.  \n\n**As well as** including the TapHoldManager library, include `InterceptionTapHold.ahk` and `AutoHotInterception.ahk`.  \nThere are many ways to do this - you could either have one Lib folder next to the script containing the contents of both the AHI Lib folder and the THM Lib folder, and use:\n\n```autohotkey\n#include Lib\\TapHoldManager.ahk\n#include Lib\\InterceptionTapHold.ahk\n#include Lib\\AutoHotInterception.ahk\n```\n\nOr, copy the contents of both the AHI and THM Lib folders to `C:\\My Documents\\AutoHotkey\\Lib`, and use  \n\n```autohotkey\n#include \u003cAutoHotInterception\u003e\n#include \u003cInterceptionTapHold\u003e\n#include \u003cTapHoldManager\u003e\n```\n\nInstantiate AutoHotInterception:  \n\n```autohotkey\n;v1\nAHI := new AutoHotInterception()\n\n;v2\nAHI := AutoHotInterception()\n```\n\nGet the ID of your device:  \n\n```autohotkey\n;v1 / v2\nkeyboard1Id := AHI.GetKeyboardId(0x03EB, 0xFF02)\n```\n\n\n\n\nInstantiate `InterceptionTapHold` **instead of** `TapHoldManager` and pass in the AHI instance and the id of the device:  \n\n```autohotkey\n;v1\nITH := new InterceptionTapHold(\u003cAHI Instance\u003e, \u003cDevice ID\u003e [, \u003ctapTime\u003e, \u003choldTime\u003e, \u003cmaxTaps\u003e, \u003cblock\u003e])\n\n;v2\nITH := InterceptionTapHold(\u003cAHI Instance\u003e, \u003cDevice ID\u003e [, \u003ctapTime\u003e, \u003choldTime\u003e, \u003cmaxTaps\u003e, \u003cblock\u003e])\n```\n\neg\n\n```autohotkey\n;v1\nITH := new InterceptionTapHold(AHI, keyboard1Id)\n\n;v2\nITH := InterceptionTapHold(AHI, keyboard1Id)\n```\n\n**Required Parameters**  \n`AHI Instance` = An Instance of the AutoHotInterception class  \n`Device ID` = The ID of the device to subscribe to  \n\n**Optional Parameters**  \nThe usual THM parameters, but `window` is replaced with `block`.\n\n`block` = whether or not to block the input. Defaults to true.  \n\nNote: If you wish to use one script to handle multiple keyboards, use one manager per keyboard.  \n\n```\nITH1 := new InterceptionTapHold(AHI, keyboardId1)\nITH2 := new InterceptionTapHold(AHI, keyboardId2)\n```\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2FevilC%2FTapHoldManager","html_url":"https://awesome.ecosyste.ms/projects/github.com%2FevilC%2FTapHoldManager","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2FevilC%2FTapHoldManager/lists"}