mirror of https://github.com/zmkfirmware/zmk.git
docs: Update hold-tap page (#2888)
Co-authored-by: Cem Aksoylar <caksoylar@users.noreply.github.com>
This commit is contained in:
parent
5bb39ec3ea
commit
49f86f7ed0
File diff suppressed because one or more lines are too long
|
Before Width: | Height: | Size: 50 KiB After Width: | Height: | Size: 41 KiB |
|
|
@ -104,7 +104,7 @@ You can use the following nodes to tweak the default behaviors:
|
|||
| Node | Behavior |
|
||||
| ----- | ----------------------------------------------------- |
|
||||
| `<` | [Layer-tap](../keymaps/behaviors/layers.md#layer-tap) |
|
||||
| `&mt` | [Mod-tap](../keymaps/behaviors/mod-tap.md) |
|
||||
| `&mt` | [Mod-tap](../keymaps/behaviors/hold-tap.mdx#mod-tap) |
|
||||
|
||||
## Key Repeat
|
||||
|
||||
|
|
|
|||
|
|
@ -556,7 +556,6 @@ Consider the following prompts when writing documentation for new behaviors:
|
|||
- What does it do? Describe some general use-cases for the behavior.
|
||||
- Which properties included in the [devicetree binding](#creating-the-devicetree-binding-yaml) should be configured manually by the user? What do they do, and if applicable, what are their default values?
|
||||
- What does an example implementation in a keymap look like? Include a code-snippet of the example implementation in the keymap file's `behaviors` node.
|
||||
- Are there any [common use-cases of the behavior](#defining-common-use-cases-for-the-behavior-dtsi-optional)? Consider making a separate documentation page for these predefined variations, like how the [mod-tap](../keymaps/behaviors/mod-tap.md) has a separate page from the [hold-tap](../keymaps/behaviors/hold-tap.mdx).
|
||||
- How does the behavior perform in edge cases? For example, tap-dances invoke the last binding in its list of `bindings` once the maximum number of keypresses has been reached.
|
||||
|
||||
Consider also including visual aids alongside written documentation if it adds clarity.
|
||||
|
|
|
|||
|
|
@ -52,19 +52,19 @@ Below table lists major features/capabilities currently supported in ZMK, as wel
|
|||
| [Multi-Device BLE Connectivity](features/bluetooth.md#profiles) | ✅ |
|
||||
| [USB Connectivity](keymaps/behaviors/outputs.md) | ✅ |
|
||||
|
||||
| Keymap Features | Support |
|
||||
| ------------------------------------------------------------------------------------------------------------------------------------------------------ | :-----: |
|
||||
| [User Configuration Repositories](user-setup.mdx) | ✅ |
|
||||
| [Keymaps and Layers](keymaps/index.mdx) | ✅ |
|
||||
| [Wide Range of Keycodes](keymaps/list-of-keycodes.mdx) | ✅ |
|
||||
| [Flexible Behavior System](keymaps/behaviors/index.mdx) | ✅ |
|
||||
| [Hold-Taps](keymaps/behaviors/hold-tap.mdx) (including [Mod-Tap](keymaps/behaviors/mod-tap.md) and [Layer-Tap](keymaps/behaviors/layers.md#layer-tap)) | ✅ |
|
||||
| [Tap-Dances](keymaps/behaviors/tap-dance.mdx) | ✅ |
|
||||
| [Sticky (One Shot) Keys](keymaps/behaviors/sticky-key.md) | ✅ |
|
||||
| [Combos](keymaps/combos.md) | ✅ |
|
||||
| [Macros](keymaps/behaviors/macros.md) | ✅ |
|
||||
| [Mouse Keys](keymaps/behaviors/mouse-emulation.md) | ✅ |
|
||||
| [Realtime Keymap Updating](features/studio.md) | 🚧 |
|
||||
| Keymap Features | Support |
|
||||
| ------------------------------------------------------------------------------------------------------------------------------------------------------------------- | :-----: |
|
||||
| [User Configuration Repositories](user-setup.mdx) | ✅ |
|
||||
| [Keymaps and Layers](keymaps/index.mdx) | ✅ |
|
||||
| [Wide Range of Keycodes](keymaps/list-of-keycodes.mdx) | ✅ |
|
||||
| [Flexible Behavior System](keymaps/behaviors/index.mdx) | ✅ |
|
||||
| [Hold-Taps](keymaps/behaviors/hold-tap.mdx) (including [Mod-Tap](keymaps/behaviors/hold-tap.mdx#mod-tap) and [Layer-Tap](keymaps/behaviors/hold-tap.mdx#layer-tap)) | ✅ |
|
||||
| [Tap-Dances](keymaps/behaviors/tap-dance.mdx) | ✅ |
|
||||
| [Sticky (One Shot) Keys](keymaps/behaviors/sticky-key.md) | ✅ |
|
||||
| [Combos](keymaps/combos.md) | ✅ |
|
||||
| [Macros](keymaps/behaviors/macros.md) | ✅ |
|
||||
| [Mouse Keys](keymaps/behaviors/mouse-emulation.md) | ✅ |
|
||||
| [Realtime Keymap Updating](features/studio.md) | 🚧 |
|
||||
|
||||
</Column>
|
||||
</Columns>
|
||||
|
|
|
|||
|
|
@ -5,7 +5,7 @@ sidebar_label: Caps Word
|
|||
|
||||
## Summary
|
||||
|
||||
The caps word behavior behaves similar to a caps lock, but will automatically deactivate when any key not in a continue list is pressed, or if the caps word key is pressed again. For smaller keyboards using [mod-taps](mod-tap.md), this can help avoid repeated alternating holds when typing words in all caps.
|
||||
The caps word behavior behaves similar to a caps lock, but will automatically deactivate when any key not in a continue list is pressed, or if the caps word key is pressed again. For smaller keyboards using [mod-taps](hold-tap.mdx#mod-tap), this can help avoid repeated alternating holds when typing words in all caps.
|
||||
|
||||
The modifiers are applied only to the alphabetic (`A` to `Z`) keycodes, to avoid automatically applying them to numeric values, etc.
|
||||
|
||||
|
|
|
|||
|
|
@ -8,261 +8,187 @@ import TabItem from "@theme/TabItem";
|
|||
|
||||
## Summary
|
||||
|
||||
Hold-tap is the basis for other behaviors such as layer-tap and mod-tap.
|
||||
A hold-tap behavior is defined using a "hold" behavior and a "tap" behavior.
|
||||
When the key is held, then it will output the hold behavior, and when it is tapped it will output the tap behavior.
|
||||
|
||||
Simply put, the hold-tap key will output the 'hold' behavior if it's held for a while, and output the 'tap' behavior when it's tapped quickly.
|
||||
Various configuration options exist to allow fine-tuning of whether a particular key press will resolve in a hold or in a tap.
|
||||
Such configuration options determine how to treat "interrupts" when one key is pressed while a hold-tap has not been released yet, or how long a key should be held before being treated as a hold instead of a tap, and so on.
|
||||
|
||||
### Hold-Tap
|
||||
ZMK predefines two hold-tap behaviors: [mod-tap](#mod-tap) and [layer-tap](#layer-tap).
|
||||
|
||||
The graph below shows how the hold-tap decides between a 'tap' and a 'hold'.
|
||||
## Mod-Tap
|
||||
|
||||
The "mod-tap" behavior sends a different [key press](key-press.md), depending on whether it's held or tapped.
|
||||
|
||||
- If you hold the key for longer than 200ms or press any other key while it is held, the first keycode ("mod") is sent.
|
||||
- If you tap the key (release before 200ms), the second keycode ("tap") is sent.
|
||||
|
||||

|
||||
|
||||
By default, the hold-tap is configured to also select the 'hold' functionality if another key is tapped while it's active:
|
||||
By default, mod-tap is configured with the ["hold-preferred" `flavor`](#interrupt-flavors).
|
||||
|
||||

|
||||
### Behavior Binding
|
||||
|
||||
We call this the 'hold-preferred' flavor of hold-taps. While this flavor may work very well for a ctrl/escape key, it's not very well suited for home-row mods or layer-taps. That's why there are two more flavors to choose from: 'tap-preferred' and 'balanced'.
|
||||
- Reference: `&mt`
|
||||
- Parameter #1: The keycode to be sent when held (usually a modifier), e.g. `LSHIFT`
|
||||
- Parameter #2: The keycode to sent when used as a tap, e.g. `A`, `B`.
|
||||
|
||||
#### Flavors
|
||||
|
||||
- The 'hold-preferred' flavor triggers the hold behavior when the `tapping-term-ms` has expired or another key is pressed.
|
||||
- The 'balanced' flavor will trigger the hold behavior when the `tapping-term-ms` has expired or another key is pressed and released.
|
||||
- The 'tap-preferred' flavor triggers the hold behavior when the `tapping-term-ms` has expired. Pressing another key within `tapping-term-ms` does not affect the decision.
|
||||
- The 'tap-unless-interrupted' flavor triggers a hold behavior only when another key is pressed before `tapping-term-ms` has expired. It triggers the tap behavior in all other situations.
|
||||
|
||||
When the hold-tap key is released and the hold behavior has not been triggered, the tap behavior will trigger.
|
||||
|
||||

|
||||
|
||||
### Basic Usage
|
||||
|
||||
For basic usage, please see the [mod-tap](mod-tap.md) and [layer-tap](layers.md#layer-tap) pages.
|
||||
|
||||
### Advanced Configuration
|
||||
|
||||
#### `tapping-term-ms`
|
||||
|
||||
Defines how long a key must be pressed to trigger Hold behavior.
|
||||
|
||||
#### `quick-tap-ms`
|
||||
|
||||
If you press a tapped hold-tap again within `quick-tap-ms` milliseconds of the first press, it will always trigger the tap behavior. This is useful for things like a backspace, where a quick tap+hold holds backspace pressed. Set this to a negative value to disable. The default is -1 (disabled).
|
||||
|
||||
#### `require-prior-idle-ms`
|
||||
|
||||
`require-prior-idle-ms` is like `quick-tap-ms` however it will apply for _any_ non-modifier key pressed before it. This effectively disables the hold-tap when typing quickly, which can be quite useful for homerow mods. It can also have the effect of removing the input delay when typing quickly.
|
||||
|
||||
For example, the following hold-tap configuration enables `require-prior-idle-ms` with a 125 millisecond term, alongside `quick-tap-ms` with a 200 millisecond term.
|
||||
Example:
|
||||
|
||||
```dts
|
||||
rpi: require_prior_idle {
|
||||
compatible = "zmk,behavior-hold-tap";
|
||||
#binding-cells = <2>;
|
||||
flavor = "tap-preferred";
|
||||
tapping-term-ms = <200>;
|
||||
quick-tap-ms = <200>;
|
||||
require-prior-idle-ms = <125>;
|
||||
bindings = <&kp>, <&kp>;
|
||||
};
|
||||
&mt LSHIFT A
|
||||
```
|
||||
|
||||
If you press `&kp A` and then `&rpi LEFT_SHIFT B` **within** 125 ms, then `ab` will be output. Importantly, `b` will be output immediately since it was within the `require-prior-idle-ms`, without waiting for a timeout or an interrupting key. In other words, the `&rpi LEFT_SHIFT B` binding will only have its underlying hold-tap behavior if it is pressed 125 ms **after** the previous key press; otherwise it will act like `&kp B`.
|
||||
### Configuration
|
||||
|
||||
Note that the greater the value of `require-prior-idle-ms` is, the harder it will be to invoke the hold behavior, making this feature less applicable for use-cases like capitalizing letters while typing normally. However, if the hold behavior isn't used during fast typing, then it can be an effective way to mitigate misfires.
|
||||
|
||||
#### `retro-tap`
|
||||
|
||||
If `retro-tap` is enabled, the tap behavior is triggered when releasing the hold-tap key if no other key was pressed in the meantime. The hold key does not activate until another key is pressed, meaning that it cannot be used for mouse events like Shift Click to select from your cursor position to mouse position.
|
||||
|
||||
For example, if you press `&mt LEFT_SHIFT A` and then release it without pressing another key, it will output `a`.
|
||||
You can adjust the default [properties](#custom-hold-tap-configuration) of the mod-tap behavior using its node like so:
|
||||
|
||||
```dts
|
||||
&mt {
|
||||
retro-tap;
|
||||
tapping-term-ms = <200>; // This is the value already set by default
|
||||
};
|
||||
```
|
||||
|
||||
#### `hold-while-undecided`
|
||||
Note that you can also [define a custom hold-tap](#custom-hold-tap-examples) and use that, instead of adjusting the properties of the mod-tap/`&mt` behavior.
|
||||
|
||||
If enabled, the hold behavior will immediately be held on hold-tap press, and will release before the behavior is sent in the event the hold-tap resolves into a tap. With most modifiers this will not affect typing, and is useful for using modifiers with the mouse.
|
||||
## Layer-Tap
|
||||
|
||||
:::info[Alt/Win/Cmd behavior]
|
||||
In some applications/desktop environments, pressing Alt keycodes by itself will have its own behavior like activate a menu and Gui keycodes will bring up the start menu or an application launcher.
|
||||
:::
|
||||
The "layer-tap" behavior works identically to the mod-tap behavior, but instead of outputting one of two keys, it activates a specified layer as its "hold" action.
|
||||
|
||||
#### `hold-while-undecided-linger`
|
||||
### Behavior Binding
|
||||
|
||||
If your tap behavior activates the same modifier as the hold behavior, and you want to avoid a double tap when transitioning from the hold to the tap, you can use `hold-while-undecided-linger`. When enabled, the hold behavior will continue to be held until _after_ the tap behavior is released. For example, if the hold is `&kp LGUI` and the tap is `&sk LGUI`, then with `hold-while-undecided-linger` enabled, the host will see `LGUI` held down continuously until the sticky key is finished, instead of seeing a release and press when transitioning from hold to sticky key.
|
||||
- Reference: `<`
|
||||
- Parameter: The layer number to enable while held, e.g. `1`
|
||||
- Parameter: The keycode to send when tapped, e.g. `A`
|
||||
|
||||
#### Positional hold-tap and `hold-trigger-key-positions`
|
||||
|
||||
Including `hold-trigger-key-positions` in your hold-tap definition turns on the positional hold-tap feature. With positional hold-tap enabled, if you press any key **NOT** listed in `hold-trigger-key-positions` before `tapping-term-ms` expires, it will produce a tap.
|
||||
|
||||
In all other situations, positional hold-tap will not modify the behavior of your hold-tap. Positional hold-tap is useful when used with home-row modifiers: for example, if you have a home-row modifier key in the left hand, by including only key positions from the right hand in `hold-trigger-key-positions`, you will only get hold behaviors during cross-hand key combinations.
|
||||
|
||||
:::info
|
||||
Note that `hold-trigger-key-positions` is an array of key position indexes. Key positions are numbered sequentially according to your keymap, starting with 0. So if the first key in your keymap is Q, this key is in position 0. The next key (probably W) will be in position 1, et cetera.
|
||||
:::
|
||||
|
||||
See the following example, which uses a hold-tap behavior definition, configured with the `hold-preferred` flavor, and with positional hold-tap enabled:
|
||||
Example:
|
||||
|
||||
```dts
|
||||
#include <dt-bindings/zmk/keys.h>
|
||||
#include <behaviors.dtsi>
|
||||
< 3 SPACE
|
||||
```
|
||||
|
||||
/ {
|
||||
behaviors {
|
||||
pht: positional_hold_tap {
|
||||
compatible = "zmk,behavior-hold-tap";
|
||||
#binding-cells = <2>;
|
||||
flavor = "hold-preferred";
|
||||
tapping-term-ms = <400>;
|
||||
quick-tap-ms = <200>;
|
||||
bindings = <&kp>, <&kp>;
|
||||
hold-trigger-key-positions = <1>; // <---[[the W key]]
|
||||
};
|
||||
};
|
||||
keymap {
|
||||
compatible = "zmk,keymap";
|
||||
default_layer {
|
||||
bindings = <
|
||||
// position 0 position 1 position 2
|
||||
&pht LEFT_SHIFT Q &kp W &kp E
|
||||
>;
|
||||
};
|
||||
};
|
||||
### Configuration
|
||||
|
||||
You can adjust the default [properties](#custom-hold-tap-configuration) of the layer-tap behavior using its node like so:
|
||||
|
||||
```dts
|
||||
< {
|
||||
tapping-term-ms = <200>; // This is the value already set by default
|
||||
};
|
||||
```
|
||||
|
||||
- The sequence `(pht_down, E_down, E_up, pht_up)` produces `qe`. The normal hold behavior (LEFT_SHIFT) **IS** modified into a tap behavior (Q) by positional hold-tap because the first key pressed after the hold-tap key is the `E key`, which is in position 2, which **is NOT** included in `hold-trigger-key-positions`.
|
||||
- The sequence `(pht_down, W_down, W_up, pht_up)` produces `W`. The normal hold behavior (LEFT_SHIFT) **is NOT** modified into a tap behavior (Q) by positional hold-tap because the first key pressed after the hold-tap key is the `W key`, which is in position 1, which **IS** included in `hold-trigger-key-positions`.
|
||||
- If the `LEFT_SHIFT / Q key` is held by itself for longer than `tapping-term-ms`, a hold behavior is produced. This is because positional hold-tap only modifies the behavior of a hold-tap if another key is pressed before the `tapping-term-ms` period expires.
|
||||
Note that you can also [define a custom hold-tap](#custom-hold-tap-examples) and use that, instead of adjusting the properties of the layer-tap/`<` behavior.
|
||||
|
||||
By default, `hold-trigger-key-positions` are evaluated upon the first _key press_ after
|
||||
the hold-tap. For homerow mods, this is not always ideal, because it prevents combining multiple modifiers unless they are included in `hold-trigger-key-positions`. To overwrite this behavior, one can set `hold-trigger-on-release`. If set to true, the evaluation of `hold-trigger-key-positions` gets delayed until _key release_. This allows combining multiple modifiers when the next key is _held_, while still deciding the hold-tap in favor of a tap when the next key is _tapped_.
|
||||
## Custom Hold-Tap Examples
|
||||
|
||||
#### Using different behavior types with hold-taps
|
||||
|
||||
You can create instances of hold-taps invoking most [behavior types](../index.mdx#behaviors) for hold or tap actions, by referencing their node labels in the `bindings` value.
|
||||
The two parameters that are passed to the hold-tap in your keymap will be forwarded to the referred behaviors, first one to the hold behavior and second one to the tap.
|
||||
|
||||
If you use behaviors that accept no parameters such as [mod-morphs](mod-morph.md) or [macros](macros.md), you can pass a dummy parameter value such as `0` to the hold-tap when you use it in your keymap.
|
||||
For instance, a hold-tap with node label `caps` and `bindings = <&kp>, <&caps_word>;` can be used in the keymap as below to send the caps lock keycode on hold and invoke the [caps word behavior](caps-word.md) on tap:
|
||||
|
||||
```dts
|
||||
&caps CAPS 0
|
||||
```
|
||||
|
||||
:::info
|
||||
You cannot use behaviors that expect more than one parameter such as [`&bt`](bluetooth.md) and [`&rgb_ug`](underglow.md) with hold-taps, due to the limitations of the [devicetree keymap format](../../config/index.md#devicetree-files).
|
||||
One workaround is to create a [macro](macros.md) that invokes those behaviors and use the macro as the hold or tap action.
|
||||
:::
|
||||
|
||||
### Example Use-Cases
|
||||
To see what specific configuration options do, please see the [configuration options](#custom-hold-tap-configuration).
|
||||
The below examples are intended for you to copy-paste as desired, and then tune with the configuration options.
|
||||
|
||||
<Tabs
|
||||
queryString="examples"
|
||||
defaultValue="homerow_mods"
|
||||
defaultValue="mlt"
|
||||
values={[
|
||||
{label: 'Homerow Mods', value: 'homerow_mods'},
|
||||
{label: 'Mod/Layer-tap', value: 'mlt'},
|
||||
{label: 'Homerow Mods', value: 'home_row_mods'},
|
||||
{label: 'Autoshift', value: 'autoshift'},
|
||||
{label: 'Toggle-on-Tap, Momentary-on-Hold Layers', value: 'mo_tog'},
|
||||
{label: 'Tog-Tap, Mo-Hold Layers', value: 'mo_tog'},
|
||||
]}>
|
||||
|
||||
<TabItem value="homerow_mods">
|
||||
<TabItem value="mlt">
|
||||
|
||||
The following are suggested hold-tap configurations that work well with home row mods:
|
||||
The default configuration of mod-tap and layer-tap can be found below:
|
||||
|
||||
##### Option 1: cross-hand only modifiers, using `tap-unless-interrupted` and positional hold-tap (`hold-trigger-key-positions`)
|
||||
|
||||
```dts title="Homerow Mods: Cross-hand Example"
|
||||
#include <dt-bindings/zmk/keys.h>
|
||||
#include <behaviors.dtsi>
|
||||
```dts title="Mod-tap"
|
||||
#include <dt-bindings/zmk/behaviors.h>
|
||||
|
||||
/ {
|
||||
behaviors {
|
||||
lh_pht: left_positional_hold_tap {
|
||||
mt: mod_tap {
|
||||
compatible = "zmk,behavior-hold-tap";
|
||||
#binding-cells = <2>;
|
||||
flavor = "tap-unless-interrupted";
|
||||
tapping-term-ms = <100>; // <---[[produces tap if held longer than tapping-term-ms]]
|
||||
quick-tap-ms = <200>;
|
||||
flavor = "hold-preferred";
|
||||
tapping-term-ms = <200>;
|
||||
bindings = <&kp>, <&kp>;
|
||||
hold-trigger-key-positions = <5 6 7 8 9 10>; // <---[[right-hand keys]]
|
||||
};
|
||||
};
|
||||
|
||||
keymap {
|
||||
compatible = "zmk,keymap";
|
||||
default_layer {
|
||||
bindings = <
|
||||
// position 0 pos 1 pos 2 pos 3 pos 4 pos 5 pos 6 pos 7 pos 8 pos 9 pos 10
|
||||
&lh_pht LSFT A &lh_pht LGUI S &lh_pht LALT D &lh_pht LCTL F &kp G &kp H &kp I &kp J &kp K &kp L &kp SEMI
|
||||
>;
|
||||
display-name = "Mod-Tap";
|
||||
};
|
||||
};
|
||||
};
|
||||
|
||||
```
|
||||
|
||||
##### Option 2: `tap-preferred`
|
||||
|
||||
```dts title="Homerow Mods: Tap-Preferred Example"
|
||||
#include <behaviors.dtsi>
|
||||
#include <dt-bindings/zmk/keys.h>
|
||||
```dts title="Layer-tap"
|
||||
#include <dt-bindings/zmk/behaviors.h>
|
||||
|
||||
/ {
|
||||
behaviors {
|
||||
hm: homerow_mods {
|
||||
lt: layer_tap {
|
||||
compatible = "zmk,behavior-hold-tap";
|
||||
#binding-cells = <2>;
|
||||
tapping-term-ms = <150>;
|
||||
quick-tap-ms = <0>;
|
||||
flavor = "tap-preferred";
|
||||
bindings = <&kp>, <&kp>;
|
||||
};
|
||||
};
|
||||
|
||||
keymap {
|
||||
compatible = "zmk,keymap";
|
||||
default_layer {
|
||||
bindings = <
|
||||
&hm LCTRL A &hm LGUI S &hm LALT D &hm LSHIFT F
|
||||
>;
|
||||
tapping-term-ms = <200>;
|
||||
bindings = <&mo>, <&kp>;
|
||||
display-name = "Layer-Tap";
|
||||
};
|
||||
};
|
||||
};
|
||||
|
||||
```
|
||||
|
||||
##### Option 3: `balanced`
|
||||
If you copy-paste these as-is, you will overwrite the predefined behaviors.
|
||||
Name and label them something else, e.g. `my_mt: my_mod_tap`, to avoid this.
|
||||
You'll then be able to use your new behavior with `&my_mt`.
|
||||
|
||||
```dts title="Homerow Mods: Balanced Example"
|
||||
#include <behaviors.dtsi>
|
||||
#include <dt-bindings/zmk/keys.h>
|
||||
</TabItem>
|
||||
<TabItem value="home_row_mods">
|
||||
|
||||
The most popular form of home-row mods is known as "timeless home-row mods", configured to minimize the dependency on timing.
|
||||
Timeless home-row mods define both a "left hand" and a "right hand" behavior.
|
||||
|
||||
Below is an example of a left hand behavior:
|
||||
|
||||
```dts
|
||||
/ {
|
||||
behaviors {
|
||||
bhm: balanced_homerow_mods {
|
||||
hml: home_row_mod_left {
|
||||
compatible = "zmk,behavior-hold-tap";
|
||||
#binding-cells = <2>;
|
||||
tapping-term-ms = <200>; // <---[[moderate duration]]
|
||||
quick-tap-ms = <0>;
|
||||
flavor = "balanced";
|
||||
require-prior-idle-ms = <150>;
|
||||
tapping-term-ms = <280>;
|
||||
quick-tap-ms = <175>;
|
||||
bindings = <&kp>, <&kp>;
|
||||
hold-trigger-key-positions = < ... >; // List of keys on the right side of the keyboard
|
||||
hold-trigger-on-release;
|
||||
};
|
||||
};
|
||||
|
||||
keymap {
|
||||
compatible = "zmk,keymap";
|
||||
default_layer {
|
||||
bindings = <
|
||||
&bhm LCTRL A &bhm LGUI S &bhm LALT D &bhm LSHIFT F
|
||||
>;
|
||||
hmr: home_row_mod_right {
|
||||
compatible = "zmk,behavior-hold-tap";
|
||||
#binding-cells = <2>;
|
||||
flavor = "balanced";
|
||||
require-prior-idle-ms = <150>;
|
||||
tapping-term-ms = <280>;
|
||||
quick-tap-ms = <175>;
|
||||
bindings = <&kp>, <&kp>;
|
||||
hold-trigger-key-positions = < ... >; // List of keys on the left side of the keyboard
|
||||
hold-trigger-on-release;
|
||||
};
|
||||
};
|
||||
};
|
||||
```
|
||||
|
||||
If you wish to use this configuration of home-row mods, you will need to define the corresponding left and right behaviors as above, then use `&hml` for home-row keys on the left side of the keyboard, and `&hmr` for ones on the right side.
|
||||
|
||||
Below is a brief overview of the options set.
|
||||
|
||||
- The ["balanced" flavor](#interrupt-flavors) produces a "hold" if another key is both pressed and released within the tapping-term.
|
||||
This interrupt flavor is primarily used to decide between hold and tap, rather than using [tapping-term-ms](#tapping-term-ms).
|
||||
This matches how modifiers are used a majority of the time (hold shift, tap a key, release shift).
|
||||
- [`require-prior-idle-ms`](#require-prior-idle-ms) is set to 125ms to resolve to taps immediately when typing at speed, which helps eliminate typing delay.
|
||||
- [Positional hold taps](#positional-hold-tap-and-hold-trigger-key-positions) with [`hold-trigger-on-release`] are used to avoid accidental hold resolutions when typing sequences of letters all with the same hand.
|
||||
- [`tapping-term-ms`](#tapping-term-ms) is set to a higher value of 280ms, but not unreasonably high, so that same hand modifiers can still be used with intention.
|
||||
- [`quick-tap-ms`](#quick-tap-ms) is set to 175ms, for the event where a tapped key may wish to be held down.
|
||||
|
||||
Depending on your typing behavior, you may wish to adjust this configuration. For example, you can adjust the specific timings used, or define another set of hold-taps for more dextrous fingers like the index with different parameters. You may even want to switch the flavor to `tap-preferred` or `tap-unless-interrupted`.
|
||||
|
||||
</TabItem>
|
||||
|
||||
<TabItem value="autoshift">
|
||||
|
|
@ -337,6 +263,198 @@ This hold-tap example implements a [momentary-layer](layers.md#momentary-layer)
|
|||
|
||||
</Tabs>
|
||||
|
||||
### Comparison to QMK
|
||||
## Custom Hold-Tap Configuration
|
||||
|
||||
The `hold-preferred` flavor works similar to the `HOLD_ON_OTHER_KEY_PRESS` setting in QMK. The `balanced` flavor is similar to the `PERMISSIVE_HOLD` setting, and the `tap-preferred` flavor is the QMK default.
|
||||
### `tapping-term-ms`
|
||||
|
||||
This value defines how long a key must be pressed to trigger the "hold" behavior, alongside other factors described in [interrupt flavors](#interrupt-flavors). The default is 200ms.
|
||||
|
||||
```dts
|
||||
&mt {
|
||||
tapping-term-ms = <140>;
|
||||
};
|
||||
```
|
||||
|
||||
### Using different behavior types with hold-taps
|
||||
|
||||
You can create instances of hold-taps invoking most [behavior types](../index.mdx#behaviors) for hold or tap actions, by referencing their node labels in the `bindings` value.
|
||||
The two parameters that are passed to the hold-tap in your keymap will be forwarded to the referred behaviors, first one to the hold behavior and second one to the tap.
|
||||
|
||||
If you use behaviors that accept no parameters such as [mod-morphs](mod-morph.md) or [tap-dances](tap-dance.mdx), you can pass a dummy parameter value such as `0` to the hold-tap when you use it in your keymap.
|
||||
For instance, a hold-tap with node label `caps` and `bindings = <&kp>, <&caps_word>;` can be used in the keymap as below to send the caps lock keycode on hold and invoke the [caps word behavior](caps-word.md) on tap:
|
||||
|
||||
```dts
|
||||
&caps CAPS 0
|
||||
```
|
||||
|
||||
:::info
|
||||
You cannot use behaviors that expect more than one parameter such as [`&bt`](bluetooth.md) and [`&rgb_ug`](underglow.md) with hold-taps, due to the limitations of the [devicetree keymap format](../../config/index.md#devicetree-files).
|
||||
One workaround is to create a [macro](macros.md) that invokes those behaviors and use the macro as the hold or tap action.
|
||||
:::
|
||||
|
||||
### Interrupt Flavors
|
||||
|
||||
By default, when another key is pressed while a hold-tap is held, it will trigger the "hold" behavior even if `tapping-term-ms` has not been exceeded yet.
|
||||
We refer to the interaction of pressing one key while another is held as the "interrupt", and the way the hold-tap resolves is referred to as its "interrupt flavor".
|
||||
|
||||
This default interrupt flavor is called "hold-preferred". While this flavor may work well for a ctrl/escape key, but it might not be well suited for home-row mods or layer-taps. For this reason, ZMK defines multiple interrupt flavors which hold-tap behaviors can be configured with, listed below:
|
||||
|
||||
- The "hold-preferred" flavor triggers the hold behavior when the `tapping-term-ms` has expired or another key is pressed.
|
||||
- The "balanced" flavor will trigger the hold behavior when the `tapping-term-ms` has expired or another key is pressed _and_ released while the hold-tap is held.
|
||||
- The "tap-preferred" flavor triggers the hold behavior when the `tapping-term-ms` has expired. Pressing another key within `tapping-term-ms` does not affect the decision.
|
||||
- The "tap-unless-interrupted" flavor triggers a hold behavior only when another key is pressed before `tapping-term-ms` has expired. It triggers the tap behavior in all other situations. Note that this flavor _inverts_ the decision logic with respect to the tapping term.
|
||||
|
||||
When the hold-tap key is released and the hold behavior has not been triggered, the tap behavior will trigger.
|
||||
|
||||

|
||||
|
||||
#### Comparison to QMK
|
||||
|
||||
The `hold-preferred` flavor works similar to the `HOLD_ON_OTHER_KEY_PRESS` setting in QMK.
|
||||
The `balanced` flavor is similar to the `PERMISSIVE_HOLD` setting, and the `tap-preferred` flavor is the QMK default.
|
||||
|
||||
```dts
|
||||
&mt {
|
||||
flavor = "balanced";
|
||||
};
|
||||
```
|
||||
|
||||
### `quick-tap-ms`
|
||||
|
||||
If you press a tapped hold-tap again within `quick-tap-ms` milliseconds of the first press, it will always trigger the tap behavior.
|
||||
This is useful for keys like backspace, where a quick tap-then-hold can be used to hold it down to delete long parts of text.
|
||||
By default this behavior is disabled.
|
||||
|
||||
```dts
|
||||
&mt {
|
||||
quick-tap-ms = <150>;
|
||||
};
|
||||
```
|
||||
|
||||
### `require-prior-idle-ms`
|
||||
|
||||
If a hold-tap is pressed within `require-prior-idle-ms` of another non-modifier _key_ (not behavior), then the hold-tap will always resolve in a tap.
|
||||
This effectively disables the hold-tap when typing quickly, which can be quite useful for home-row mods.
|
||||
It can also have the effect of removing the input delay when typing quickly, since the hold-tap immediately resolves to a tap on key press.
|
||||
|
||||
<details>
|
||||
<summary>The following hold-tap configuration enables `require-prior-idle-ms` with a 125 millisecond term, alongside `quick-tap-ms` with a 200 millisecond term.</summary>
|
||||
```dts
|
||||
rpi: require_prior_idle {
|
||||
compatible = "zmk,behavior-hold-tap";
|
||||
#binding-cells = <2>;
|
||||
flavor = "tap-preferred";
|
||||
tapping-term-ms = <200>;
|
||||
quick-tap-ms = <200>;
|
||||
require-prior-idle-ms = <125>;
|
||||
bindings = <&kp>, <&kp>;
|
||||
};
|
||||
```
|
||||
|
||||
If you press `&kp A` and then `&rpi LEFT_SHIFT B` **within** 125 ms, then `ab` will be output.
|
||||
Importantly, `b` will be output immediately since it was within the `require-prior-idle-ms`, without waiting for a timeout or an interrupting key.
|
||||
In other words, the `&rpi LEFT_SHIFT B` binding will only have its underlying hold-tap behavior if it is pressed 125 ms **after** the previous key press; otherwise it will act like `&kp B`.
|
||||
|
||||
Note that the greater the value of `require-prior-idle-ms` is, the harder it will be to invoke the hold behavior, making this feature less applicable for use-cases like capitalizing letters while typing normally.
|
||||
However, if the hold behavior isn't used during fast typing, then it can be an effective way to mitigate misfires.
|
||||
|
||||
</details>
|
||||
|
||||
### Positional hold-tap and `hold-trigger-key-positions`
|
||||
|
||||
Including `hold-trigger-key-positions` in your hold-tap definition turns on the positional hold-tap feature. With positional hold-tap enabled, if you press any key **not** listed in `hold-trigger-key-positions` before `tapping-term-ms` expires, it will produce a tap.
|
||||
|
||||
In all other situations, positional hold-tap will not modify the behavior of your hold-tap. Positional hold-tap is useful when used with home-row modifiers: for example, if you have a home-row modifier key in the left hand, by including only key positions from the right hand in `hold-trigger-key-positions`, you will only get hold behaviors during cross-hand key combinations unless you exceed `tapping-term-ms` when using "balanced" or "hold-preferred" flavors.
|
||||
|
||||
For home-row mods, it is recommended to use this property with `hold-trigger-on-release` so that modifiers on the same hand can be combined.
|
||||
|
||||
:::info
|
||||
`hold-trigger-key-positions` is an array of key position indexes. Key positions are numbered sequentially according to your keymap, starting with 0. So if the first key in your keymap is Q, this key is in position 0. The next key (probably W) will be in position 1, et cetera.
|
||||
:::
|
||||
|
||||
<details>
|
||||
<summary>The following example uses a hold-tap behavior definition configured with the `hold-preferred` flavor, and with positional hold-tap enabled:</summary>
|
||||
|
||||
```dts
|
||||
#include <dt-bindings/zmk/keys.h>
|
||||
#include <behaviors.dtsi>
|
||||
|
||||
/ {
|
||||
behaviors {
|
||||
pht: positional_hold_tap {
|
||||
compatible = "zmk,behavior-hold-tap";
|
||||
#binding-cells = <2>;
|
||||
flavor = "hold-preferred";
|
||||
tapping-term-ms = <400>;
|
||||
quick-tap-ms = <200>;
|
||||
bindings = <&kp>, <&kp>;
|
||||
hold-trigger-key-positions = <1>; // <---[[the W key]]
|
||||
};
|
||||
};
|
||||
keymap {
|
||||
compatible = "zmk,keymap";
|
||||
default_layer {
|
||||
bindings = <
|
||||
// position 0 position 1 position 2
|
||||
&pht LEFT_SHIFT Q &kp W &kp E
|
||||
>;
|
||||
};
|
||||
};
|
||||
};
|
||||
```
|
||||
|
||||
- The sequence `(pht_down, E_down, E_up, pht_up)` produces `qe`. The normal hold behavior (LEFT_SHIFT) **IS** modified into a tap behavior (Q) by positional hold-tap because the first key pressed after the hold-tap key is the `E key`, which is in position 2, which **is NOT** included in `hold-trigger-key-positions`.
|
||||
- The sequence `(pht_down, W_down, W_up, pht_up)` produces `W`. The normal hold behavior (LEFT_SHIFT) **is NOT** modified into a tap behavior (Q) by positional hold-tap because the first key pressed after the hold-tap key is the `W key`, which is in position 1, which **IS** included in `hold-trigger-key-positions`.
|
||||
- If the `LEFT_SHIFT / Q key` is held by itself for longer than `tapping-term-ms`, a hold behavior is produced. This is because positional hold-tap only modifies the behavior of a hold-tap if another key is pressed before the `tapping-term-ms` period expires.
|
||||
|
||||
By default, `hold-trigger-key-positions` are evaluated upon the first _key press_ after
|
||||
the hold-tap. For home-row mods, this is not always ideal, because it prevents combining multiple modifiers unless they are included in `hold-trigger-key-positions`. To overwrite this behavior, one can set `hold-trigger-on-release`. If set to true, the evaluation of `hold-trigger-key-positions` gets delayed until _key release_. This allows combining multiple modifiers when the next key is _held_, while still deciding the hold-tap in favor of a tap when the next key is _tapped_.
|
||||
|
||||
</details>
|
||||
#### `hold-trigger-on-release`
|
||||
|
||||
If set, instead of the keys listed in `hold-trigger-key-positions` producing a tap when _pressed_ before `tapping-term-ms` expires, they instead produce a tap when _released_ before `tapping-term-ms` expires.
|
||||
|
||||
```dts
|
||||
&mt {
|
||||
hold-trigger-on-release;
|
||||
};
|
||||
```
|
||||
|
||||
### `hold-while-undecided`
|
||||
|
||||
If enabled, the hold behavior will immediately be held on hold-tap press, and will release before the behavior is sent in the event the hold-tap resolves into a tap. With most modifiers this will not affect typing, and is useful for using modifiers with the mouse.
|
||||
|
||||
```dts
|
||||
&mt {
|
||||
hold-while-undecided;
|
||||
};
|
||||
```
|
||||
|
||||
#### `hold-while-undecided-linger`
|
||||
|
||||
If your tap behavior activates the same modifier as the hold behavior, and you want to avoid a double tap when transitioning from the hold to the tap, you can use `hold-while-undecided-linger`. When enabled, the hold behavior will continue to be held until _after_ the tap behavior is released.
|
||||
|
||||
For example, if the hold is `&kp LGUI` and the tap is `&sk LGUI`, then with `hold-while-undecided-linger` enabled, the host will see `LGUI` held down continuously until the sticky key is finished, instead of seeing a release and press when transitioning from hold to sticky key.
|
||||
|
||||
```dts
|
||||
&mt {
|
||||
hold-while-undecided-linger;
|
||||
};
|
||||
```
|
||||
|
||||
:::info[Alt/Win/Cmd behavior]
|
||||
In some applications/desktop environments, pressing Alt keycodes by itself will have its own behavior like activate a menu and Gui keycodes will bring up the start menu or an application launcher.
|
||||
:::
|
||||
|
||||
### `retro-tap`
|
||||
|
||||
If `retro-tap` is enabled, the tap behavior is triggered when releasing the hold-tap key if no other key was pressed in the meantime. The hold key does not activate until another key is pressed, meaning that it cannot be used for mouse events like Shift Click to select from your cursor position to mouse position.
|
||||
|
||||
For example, if you press `&mt LEFT_SHIFT A` and then release it without pressing another key, it will output `a`.
|
||||
|
||||
```dts
|
||||
&mt {
|
||||
retro-tap;
|
||||
};
|
||||
```
|
||||
|
|
|
|||
|
|
@ -14,7 +14,7 @@ Below is a summary of pre-defined behavior bindings and user-definable behaviors
|
|||
| Binding | Behavior | Description |
|
||||
| ------------- | --------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
||||
| `&kp` | [Key Press](key-press.md) | Send keycodes to the connected host when a key is pressed |
|
||||
| `&mt` | [Mod Tap](mod-tap.md) | Sends a different key press depending on whether a key is held or tapped |
|
||||
| `&mt` | [Mod Tap](hold-tap.mdx#mod-tap) | Sends a different key press depending on whether a key is held or tapped |
|
||||
| `&kt` | [Key Toggle](key-toggle.md) | Toggles the press of a key. If the key is not currently pressed, key toggle will press it, holding it until the key toggle is pressed again or the key is released in some other way. If the key is currently pressed, key toggle will release it |
|
||||
| `&sk` | [Sticky Key](sticky-key.md) | Stays pressed until another key is pressed, then is released. It is often used for modifier keys like shift, which allows typing capital letters without holding it down |
|
||||
| `&gresc` | [Grave Escape](mod-morph.md#behavior-binding) | Sends Grave Accent `` ` `` keycode if shift or GUI is held, sends Escape keycode otherwise |
|
||||
|
|
@ -82,10 +82,10 @@ Below is a summary of pre-defined behavior bindings and user-definable behaviors
|
|||
|
||||
## User-Defined Behaviors
|
||||
|
||||
| Behavior | Description |
|
||||
| ----------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
|
||||
| [Macros](macros.md) | Allows configuring a list of other behaviors to invoke when the key is pressed and/or released |
|
||||
| [Hold-Tap](hold-tap.mdx) | Invokes different behaviors depending on key press duration or interrupting keys. This is the basis for [layer-tap](layers.md#layer-tap) and [mod-tap](mod-tap.md) |
|
||||
| [Tap Dance](tap-dance.mdx) | Invokes different behaviors corresponding to how many times a key is pressed |
|
||||
| [Mod-Morph](mod-morph.md) | Invokes different behaviors depending on whether a specified modifier is held during a key press |
|
||||
| [Sensor Rotation](sensor-rotate.md) | Invokes different behaviors depending on whether a sensor is rotated clockwise or counter-clockwise |
|
||||
| Behavior | Description |
|
||||
| ----------------------------------- | --------------------------------------------------------------------------------------------------- |
|
||||
| [Macros](macros.md) | Allows configuring a list of other behaviors to invoke when the key is pressed and/or released |
|
||||
| [Hold-Tap](hold-tap.mdx) | Invokes different behaviors depending on key press duration or interrupting keys. |
|
||||
| [Tap Dance](tap-dance.mdx) | Invokes different behaviors corresponding to how many times a key is pressed |
|
||||
| [Mod-Morph](mod-morph.md) | Invokes different behaviors depending on whether a specified modifier is held during a key press |
|
||||
| [Sensor Rotation](sensor-rotate.md) | Invokes different behaviors depending on whether a sensor is rotated clockwise or counter-clockwise |
|
||||
|
|
|
|||
|
|
@ -34,44 +34,8 @@ Example:
|
|||
|
||||
## Layer-Tap
|
||||
|
||||
The "layer-tap" behavior enables a layer when a key is held, and outputs a [keypress](key-press.md) when the key is only tapped for a short time.
|
||||
|
||||
### Behavior Binding
|
||||
|
||||
- Reference: `<`
|
||||
- Parameter: The layer number to enable while held, e.g. `1`
|
||||
- Parameter: The keycode to send when tapped, e.g. `A`
|
||||
|
||||
Example:
|
||||
|
||||
```dts
|
||||
< 3 SPACE
|
||||
```
|
||||
|
||||
### Configuration
|
||||
|
||||
You can configure a different tapping term or tweak other properties noted in the [hold-tap](hold-tap.mdx#advanced-configuration) documentation page in your keymap:
|
||||
|
||||
```dts
|
||||
< {
|
||||
tapping-term-ms = <200>;
|
||||
};
|
||||
|
||||
/ {
|
||||
keymap {
|
||||
...
|
||||
};
|
||||
};
|
||||
```
|
||||
|
||||
:::info
|
||||
Functionally, the layer-tap is a [hold-tap](hold-tap.mdx) of the ["tap-preferred" flavor](hold-tap.mdx#flavors) and a [`tapping-term-ms`](hold-tap.mdx#tapping-term-ms) of 200 that takes in a [`momentary layer`](#momentary-layer) and a [keypress](key-press.md) as its "hold" and "tap" parameters, respectively.
|
||||
|
||||
For users who want to send a different [keycode](../list-of-keycodes.mdx) depending on if the same key is held or tapped, see [Mod-Tap](mod-tap.md).
|
||||
|
||||
Similarly, for users looking to create a keybind like the layer-tap that depending on how long the key is held, invokes behaviors like [sticky keys](sticky-key.md) or [key toggles](key-toggle.md), see [Hold-Tap](hold-tap.mdx).
|
||||
|
||||
:::
|
||||
The ["layer-tap" behavior](hold-tap.mdx#layer-tap) enables a layer when a key is held, and outputs a [key press](key-press.md) when the key is only tapped for a short time.
|
||||
See linked documentation for details.
|
||||
|
||||
## To Layer
|
||||
|
||||
|
|
|
|||
|
|
@ -1,54 +0,0 @@
|
|||
---
|
||||
title: Mod-Tap Behavior
|
||||
sidebar_label: Mod-Tap
|
||||
---
|
||||
|
||||
## Summary
|
||||
|
||||
The Mod-Tap behavior sends a different keypress, depending on whether it's held or tapped.
|
||||
|
||||
- If you hold the key for longer than 200ms, the first keycode ("mod") is sent.
|
||||
- If you tap the key (release before 200ms), the second keycode ("tap") is sent.
|
||||
|
||||
If you press another key within the 200ms, the 'mod' behavior is also activated.
|
||||
|
||||
## Mod-Tap
|
||||
|
||||
The Mod-Tap behavior either acts as a held modifier, or as a tapped keycode.
|
||||
|
||||
### Behavior Binding
|
||||
|
||||
- Reference: `&mt`
|
||||
- Parameter #1: The keycode to be sent when activating as a modifier, e.g. `LSHIFT`
|
||||
- Parameter #2: The keycode to sent when used as a tap, e.g. `A`, `B`.
|
||||
|
||||
Example:
|
||||
|
||||
```dts
|
||||
&mt LSHIFT A
|
||||
```
|
||||
|
||||
### Configuration
|
||||
|
||||
You can configure a different tapping term in your keymap:
|
||||
|
||||
```dts
|
||||
&mt {
|
||||
tapping-term-ms = <400>;
|
||||
};
|
||||
|
||||
/ {
|
||||
keymap {
|
||||
...
|
||||
};
|
||||
};
|
||||
```
|
||||
|
||||
:::info
|
||||
Under the hood, the mod-tap is simply a [hold-tap](hold-tap.mdx) of the ["hold-preferred" flavor](hold-tap.mdx#flavors) with a [`tapping-term-ms`](hold-tap.mdx#tapping-term-ms) of 200 that takes in two [keypresses](key-press.md) as its "hold" and "tap" parameters. This means that the mod-tap can be used to invoke **any** [keycode](../list-of-keycodes.mdx), and is not limited to only activating [modifier keys](../modifiers.mdx) when it is held.
|
||||
|
||||
For users who want to momentarily access a specific [layer](../index.mdx#layers) while a key is held and send a keycode when the same key is tapped, see [Layer-Tap](layers.md#layer-tap).
|
||||
|
||||
Similarly, for users looking to create a keybind like the mod-tap that invokes behaviors _other_ than [keypresses](key-press.md), like [sticky keys](sticky-key.md) or [key toggles](key-toggle.md), see [Hold-Tap](hold-tap.mdx).
|
||||
|
||||
:::
|
||||
|
|
@ -67,7 +67,6 @@ module.exports = {
|
|||
"keymaps/behaviors/layers",
|
||||
"keymaps/behaviors/misc",
|
||||
"keymaps/behaviors/hold-tap",
|
||||
"keymaps/behaviors/mod-tap",
|
||||
"keymaps/behaviors/mod-morph",
|
||||
"keymaps/behaviors/macros",
|
||||
"keymaps/behaviors/key-toggle",
|
||||
|
|
|
|||
|
|
@ -22,3 +22,4 @@
|
|||
/docs/development/tests /docs/development/local-toolchain/tests 301
|
||||
/docs/development/guides/new-behavior /docs/development/new-behavior 301
|
||||
/docs/development/hardware-integration/studio-setup /docs/development/hardware-integration/physical-layouts 301
|
||||
/docs/keymaps/behaviors/mod-tap /docs/keymaps/behaviors/hold-tap#mod-tap
|
||||
Loading…
Reference in New Issue