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'.
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'.
@ -30,11 +33,11 @@ We call this the 'hold-preferred' flavor of hold-taps. While this flavor may wor
When the hold-tap key is released and the hold behavior has not been triggered, the tap behavior will trigger.
When the hold-tap key is released and the hold behavior has not been triggered, the tap behavior will trigger.
For basic usage, please see [mod-tap](mod-tap.md) and [layer-tap](layers.md) pages.
For basic usage, please see the [mod-tap](mod-tap.md) and [layer-tap](layers.md#layer-tap) pages.
### Advanced Configuration
### Advanced Configuration
@ -50,9 +53,9 @@ In QMK, unlike ZMK, this functionality is enabled by default, and you turn it of
#### `global-quick-tap`
#### `global-quick-tap`
If global quick tap is enabled, then `quick-tap-ms` will apply not only when the given hold-tap is tapped but for any key tap 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.
If `global-quick-tap` is enabled, then `quick-tap-ms` will apply not only when the given hold-tap is tapped, but for any key tapped 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 global quick tap with a 125 millisecond term.
For example, the following hold-tap configuration enables `global-quick-tap` with a 125 millisecond `quick-tap-ms` term.
```
```
gqt: global-quick-tap {
gqt: global-quick-tap {
@ -69,11 +72,11 @@ gqt: global-quick-tap {
If you press `&kp A` and then `&gqt LEFT_SHIFT B`**within** 125 ms, then `ab` will be output. Importantly, `b` will be output immediately since it was within the `quick-tap-ms`. This quick-tap behavior will work for any key press, whether it is within a behavior like hold-tap, or a simple `&kp`. This means the `&gqt LEFT_SHIFT B` binding will only have its underlying hold-tap behavior if it is pressed 125 ms **after** a key press.
If you press `&kp A` and then `&gqt LEFT_SHIFT B`**within** 125 ms, then `ab` will be output. Importantly, `b` will be output immediately since it was within the `quick-tap-ms`. This quick-tap behavior will work for any key press, whether it is within a behavior like hold-tap, or a simple `&kp`. This means the `&gqt LEFT_SHIFT B` binding will only have its underlying hold-tap behavior if it is pressed 125 ms **after** a key press.
Note that the higher the `quick-tap-ms` the harder it will be to use the hold behavior, making this less applicable for something like capitalizing letter while typing normally. However, if the hold behavior isn't used during fast typing, then it can be an effective way to mitigate misfires.
Note that the greater the value of `quick-tap-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`
#### `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.
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.
For example, if you press `&mt LEFT_SHIFT A` and then release it without pressing another key, it will output `a`.
For example, if you press `&mt LEFT_SHIFT A` and then release it without pressing another key, it will output `a`.
@ -85,16 +88,20 @@ For example, if you press `&mt LEFT_SHIFT A` and then release it without pressin
#### Positional hold-tap and `hold-trigger-key-positions`
#### 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.
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.
- 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.
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.
- Positional hold-tap is useful with home-row modifiers. If you have a home-row modifier key in the left hand for example, by including only keys positions from the right hand in `hold-trigger-key-positions`, you will only get hold behaviors during cross-hand key combinations.
- Note that `hold-trigger-key-positions` is an array of key position indexes. Key positions are numbered 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.
:::info
- See the following example, which uses a hold-tap behavior definition, configured with the `hold-preferred` flavor, and with positional hold-tap enabled:
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:
```
```
#include<dt-bindings/zmk/keys.h>
#include<dt-bindings/zmk/keys.h>
#include<behaviors.dtsi>
#include<behaviors.dtsi>
/ {
/ {
behaviors {
behaviors {
pht: positional_hold_tap {
pht: positional_hold_tap {
@ -125,15 +132,26 @@ For example, if you press `&mt LEFT_SHIFT A` and then release it without pressin
- 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`.
- 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.
- 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.
@ -188,12 +206,11 @@ The following are suggested hold-tap configurations that work well with home row
};
};
};
};
};
};
```
```
##### Option 3: `balanced`
##### Option 3: `balanced`
```
```dtsi title="Homerow Mods: Balanced Example"
#include<behaviors.dtsi>
#include<behaviors.dtsi>
#include<dt-bindings/zmk/keys.h>
#include<dt-bindings/zmk/keys.h>
@ -219,9 +236,84 @@ The following are suggested hold-tap configurations that work well with home row
};
};
};
};
};
};
```
</TabItem>
<TabItemvalue="autoshift">
A popular method of implementing Autoshift in ZMK involves a C-preprocessor macro, commonly defined as `AS(keycode)`. This macro applies the `LSHIFT` modifier to the specified `keycode` when `AS(keycode)` is held, and simply performs a [keypress](key-press.md), `&kp keycode`, when the `AS(keycode)` binding is tapped. This simplifies the use of Autoshift in a keymap, as the complete hold-tap bindings for each desired Autoshift key, as in `&as LS(<keycode 1>) <keycode 1> &as LS(<keycode 2>) <keycode 2> ... &as LS(<keycode n>) <keycode n>`, can be quite cumbersome to use when applied to a large portion of the keymap.
This hold-tap example implements a [toggle-layer](layers.md/#toggle-layer) when the keybind is tapped and a [momentary-layer](layers.md/#momentary-layer) when it is held. Similarly to the Autoshift and Sticky Hold use-cases, a `TOG_MO(layer)` macro is defined such that the `&tog` and `&mo` behaviors can target a single layer.
```dtsi title="Hold-Tap Example: Toggle layer on Tap, Momentary layer on Hold"
#include<dt-bindings/zmk/keys.h>
#include<behaviors.dtsi>
#define TOG_MO(layer) &tog_mo layer layer // Macro to apply toggle-layer-on-tap/momentary-layer-on-hold to a specific layer
/ {
behaviors {
tog_mo: behavior_mo_tog {
compatible = "zmk,behavior-hold-tap";
label = "mo_tog";
#binding-cells = <2>;
flavor = "hold-preferred";
tapping-term-ms = <200>;
bindings = <&tog>, <&mo>;
};
};
keymap {
compatible = "zmk,keymap";
default_layer {
bindings = <
&tog_mo 2 1 // &mo 2 on hold, &tog 1 on tap
TOG_MO(3) // &mo 3 on hold, &tog 3 on tap
>;
};
};
};
```
```
#### Comparison to QMK
</TabItem>
</Tabs>
### 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 similar to `IGNORE_MOD_TAP_INTERRUPT`.
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 similar to `IGNORE_MOD_TAP_INTERRUPT`.
The "layer-tap" behavior enables a layer when a key is held, and output another key when the key is only tapped for a short time. For more information on the inner workings of layer-tap, see [hold-tap](hold-tap.md).
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
### Behavior Binding
@ -57,6 +57,15 @@ Example:
< LOWER SPACE
< LOWER SPACE
```
```
:::info
Functionally, the layer-tap is a [hold-tap](hold-tap.md) of the ["tap-preferred" flavor](hold-tap.md/#flavors) and a [`tapping-term-ms`](hold-tap.md/#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](../codes/index.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.md).
:::
## To Layer
## To Layer
The "to layer" behavior enables a layer and disables _all_ other layers _except_ the default layer.
The "to layer" behavior enables a layer and disables _all_ other layers _except_ the default layer.
@ -44,6 +44,11 @@ You can configure a different tapping term in your keymap:
};
};
```
```
### Additional information
:::info
Under the hood, the mod-tap is simply a [hold-tap](hold-tap.md) of the ["hold-preferred" flavor](hold-tap.md/#flavors) with a [`tapping-term-ms`](hold-tap.md/#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](../codes/index.mdx), and is not limited to only activating [modifier keys](../codes/modifiers.mdx) when it is held.
The mod-tap is a [hold-tap](hold-tap.md) under the hood with the "hold-preferred" flavor and tapping-term-ms 200.
For users who want to momentarily access a specific [layer](../features/keymaps#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.md).
A tap-dance key invokes a different behavior (e.g. `kp`) corresponding
A tap-dance key invokes a different behavior (e.g. `kp`) corresponding to how many times it is pressed. For example, you could configure a tap-dance key that acts as `LSHIFT` if tapped once, or Caps _Lock_ if tapped twice. The expandability of the number of [`bindings`](#bindings) attached to a particular tap-dance is a great way to add more functionality to a single key, especially for keyboards with a limited number of keys. Tap-dances are completely custom, so for every unique tap-dance key,a new tap-dance must be defined in your keymap's `behaviors`.
to how many times it is pressed. For example, you could configure a
tap-dance key that acts as `LSHIFT` if tapped once, or Caps _Lock_ if tapped twice.
The expandability of the number of [`bindings`](#bindings) attached to a
particular tap-dance is a great way to add more functionality to a single key,
especially for keyboards with a limited number of keys.
Tap-dances are completely custom, so for every unique tap-dance key,
a new tap-dance must be defined in your keymap's `behaviors`.
Tap-dances are designed to resolve immediately when interrupted by another keypress.
Tap-dances are designed to resolve immediately when interrupted by another keypress. Meaning, when a keybind is pressed other than any active tap-dances, the tap-dance will activate according to the current value of its counter before the interrupting keybind is registered.
Meaning, when a keybind is pressed other than any active tap-dances,
the tap-dance will activate according to the current value of its
counter before the interrupting keybind is registered.
### Configuration
### Configuration
#### `tapping-term-ms`
#### `tapping-term-ms`
Defines the maximum elapsed time after the last tap-dance keybind press
Defines the maximum elapsed time after the last tap-dance keybind press before a binding is selected from [`bindings`](#bindings). Default value is `200`ms.
before a binding is selected from [`bindings`](#bindings).
Default value is `200`ms.
#### `bindings`
#### `bindings`
An array of one or more keybinds. This list can include [any ZMK keycode](../codes/) and bindings for ZMK behaviors.
An array of one or more keybinds. This list can include [any ZMK keycode](../codes/) and any listed ZMK behavior, like [hold-taps](hold-tap.md), or [sticky keys](sticky-key.md). The index of a keybind in the `bindings` array corresponds to the number of times the tap-dance binding is pressed. For example, in the [basic tap-dance counter](#basic-example-counter) shown below, `&kp N2` is the second binding in the array of `bindings`: we then see an output of `2` when the `td0` binding is pressed twice.
#### Example Usage
The number of bindings in this array also determines the tap-dance's maximum number of keypresses. When a tap-dance reaches its maximum number of keypresses, it will immediately invoke the last behavior in its list of `bindings`, rather than waiting for [`tapping-term-ms`](#tapping-term-ms) to expire before the output is displayed.
This example configures a tap-dance named `td0` that outputs the number of times it is pressed from 1-3.
Alphanumeric [`key press`](key-press.md) bindings, like those used for `td0`,
Alphanumeric [`key press`](key-press.md) bindings, like those used for `td0`, will release as soon as an interrupting key press occurs. For instance, if a modifier key like `LSHIFT` were to replace the `N1` binding in the last example above, it would remain pressed until `td0`'s binding is released and the output would instead be `J`. Any following alphanumeric key presses would be capitalized as long as `td0` is held down.
will release as soon as an interrupting key press occurs.
For instance, if a modifier key like `LSHIFT` were to replace the `N1`
binding in the last example above, it would remain pressed until `td0`'s
binding is released and the output would instead be `J`. Any following
alphanumeric key presses would be capitalized as long as `td0` is held down.
:::
:::
</TabItem>
<TabItemvalue="advanced">
This example configures a mod-tap inside a tap-dance named `td_mt` that outputs `CAPSLOCK` on a single tap, `LSHIFT` on a single press and hold, and `LCTRL` when the tap-dance is pressed twice.