diff --git a/docs/NEWS.md b/docs/NEWS.md index d745af99..ff2bef6d 100644 --- a/docs/NEWS.md +++ b/docs/NEWS.md @@ -12,6 +12,60 @@ See [UPGRADING.md](UPGRADING.md) for more detailed instructions about upgrading ## New features +### New OneShot features + +#### Auto-OneShot modifiers & layers + +OneShot can now treat modifiers and layer-shift keys as automatic OneShot +keys. This includes modifiers with other modifier flags applied, so it is now +very simple to turn `Key_Meh` or `Key_Hyper` into a OneShot key. The feature is +controlled by the following new functions: + +- `OneShot.toggleAutoModifiers()`: Turn auto-OneShot modifiers on or off. +- `OneShot.toggleAutoLayers()`: Turn auto-OneShot layer shifts on or off. +- `OneShot.toggleAutoOneShot()`: Both of the above. + +There are also `enable` and `disable` versions of these functions. + +Note, it is still possible to define a modifier key in the keymap that will not +automatically become a OneShot key when pressed, by applying modifier flags to +`Key_NoKey` (e.g. `LSHIFT(Key_NoKey)`). + +#### Two new special OneShot keys + +OneShot can now also turn _any_ key into a sticky key, using either of two +special `Key` values that can be inserted in the keymap. + +##### `OneShot_MetaStickyKey` + +This is a special OneShot key (it behaves like other OneShot keys), but its +effect is to make any key pressed while it is active sticky. Press +`OneShot_MetaStickyKey`, then press `X`, and `X` will become sticky. Sticky +keys can be deactivated just like other OneShot keys, by pressing them +again. This works for any key value, so use it with caution. + +##### `OneShot_ActiveStickyKey` + +Like `OneShot_ActiveStickyKey`, this key makes other keys sticky, but rather than +affecting a subsequent key, it affects any keys already held when it is +pressed. Press `X`, press `OneShot_ActiveStickyKey`, and release `X`, and `X` +will be sticky until it is pressed again to deactivate it. Again, it works on +any key value, so use with caution. + +#### LED-ActiveModColor highlighting + +With the updates to OneShot, LED-ActiveModColor now recognizes and highlights +OneShot keys in three different states (along with normal modifiers): + +- one-shot (a key that's active after release, but will time out) +- sticky (a key that will stay active indefinitely after release) +- normal (a key that will stay active only while physically held; also applies + to normal modifier keys) + +The colors of theses three highlights are controlled by the properties +`ActiveModColorEffect.oneshot_color`, `ActiveModColorEffect.sticky_color`, and +`ActiveModColorEffect.highlight_color`, respectively. + ### Better protection against unintended modifiers from Qukeys Qukeys has two new configuration options for preventing unintended modifiers in diff --git a/docs/UPGRADING.md b/docs/UPGRADING.md index 87714f0c..6203eb3f 100644 --- a/docs/UPGRADING.md +++ b/docs/UPGRADING.md @@ -18,6 +18,7 @@ If any of this does not make sense to you, or you have trouble updating your .in - [The `RxCy` macros and peeking into the keyswitch state](#the-rxcy-macros-and-peeking-into-the-keyswitch-state) - [HostOS](#hostos) - [MagicCombo](#magiccombo) + - [OneShot](#oneshot) - [Qukeys](#qukeys) - [TypingBreaks](#typingbreaks) - [Redial](#redial) @@ -580,6 +581,44 @@ If your actions made use of the `left_hand` or `right_hand` arguments of more involved to get to, out of scope for this simple migration guide. Please open an issue, or ask for help on the forums, and we'll help you. +### OneShot + +Older versions of the plugin were based on `Key` values; OneShot is now based on +`KeyAddr` coordinates instead, in order to improve reliability and +functionality. + +The following deprecated functions and variables will be removed after +**2021-04-31**. + +#### Deprecated functions + +- `OneShot.inject(key, key_state)`: This `Key`-based function still works, but + because OneShot keys are now required to have a valid `KeyAddr`, it will now + look for an idle key, and use that, masking whatever value was mapped to that + key. Most of the reasons for using this function are better addressed by using + the newer features of the plugin, such as automatic one-shot modifiers. Use is + very strongly discouraged. +- `OneShot.isActive(key)`: This `Key`-based function no longer makes sense now + that OneShot is `KeyAddr`-based. There is a `OneShot.isActive(key_addr)` + function that should be used instead. The deprecated function still works, but + its use is discouraged. +- `OneShot.isSticky(key)`: This `Key`-based function no longer makes sense now + that OneShot is `KeyAddr`-based. There is a `OneShot.isSticky(key_addr)` + function that should be used instead. The deprecated function still works, but + its use is discouraged. +- `OneShot.isPressed()`: This function no longer has any reason for existing. In + older versions, the Escape-OneShot companion plugin used it to solve a problem + that no longer exists. It now always returns `false`. +- `OneShot.isModifierActive(key)`: This function still works, but is not + perfectly reliable, because it now returns positive results for keys other + than OneShot modifiers. It should not be used. + +#### Deprecated variables + +- `OneShot.time_out`: Use `OneShot.setTimeout()` instead. +- `OneShot.hold_time_out`: Use `OneShot.setHoldTimeout()` instead. +- `OneShot.double_tap_time_out`: Use `OneShot.setDoubleTapTimeout()` instead. + ### Qukeys Older versions of the plugin used `row` and `col` indexing for defining `Qukey` diff --git a/examples/Keystrokes/OneShot/OneShot.ino b/examples/Keystrokes/OneShot/OneShot.ino index c5dfdb87..483d2c46 100644 --- a/examples/Keystrokes/OneShot/OneShot.ino +++ b/examples/Keystrokes/OneShot/OneShot.ino @@ -21,27 +21,27 @@ // Macros enum { - OSMALTCTRL, + TOGGLE_ONESHOT, }; // *INDENT-OFF* KEYMAPS( [0] = KEYMAP_STACKED ( - Key_NoKey, Key_1, Key_2, Key_3, Key_4, Key_5, Key_NoKey, + M(TOGGLE_ONESHOT), Key_1, Key_2, Key_3, Key_4, Key_5, OneShot_MetaStickyKey, Key_Backtick, Key_Q, Key_W, Key_E, Key_R, Key_T, Key_Tab, Key_PageUp, Key_A, Key_S, Key_D, Key_F, Key_G, Key_PageDown, Key_Z, Key_X, Key_C, Key_V, Key_B, Key_Escape, OSM(LeftControl), Key_Backspace, OSM(LeftGui), OSM(LeftShift), - M(OSMALTCTRL), + Key_Meh, - Key_skip, Key_6, Key_7, Key_8, Key_9, Key_0, Key_skip, + OneShot_ActiveStickyKey, Key_6, Key_7, Key_8, Key_9, Key_0, ___, Key_Enter, Key_Y, Key_U, Key_I, Key_O, Key_P, Key_Equals, Key_H, Key_J, Key_K, Key_L, Key_Semicolon, Key_Quote, - Key_skip, Key_N, Key_M, Key_Comma, Key_Period, Key_Slash, Key_Minus, + ___, Key_N, Key_M, Key_Comma, Key_Period, Key_Slash, Key_Minus, - OSM(RightShift), OSM(RightAlt), Key_Spacebar, OSM(RightControl), + Key_RightShift, Key_RightAlt, Key_Spacebar, Key_RightControl, OSL(1)), [1] = KEYMAP_STACKED @@ -64,14 +64,13 @@ KEYMAPS( ) // *INDENT-ON* -void macroOneShotAltControl(uint8_t keyState) { - OneShot.inject(OSM(LeftAlt), keyState); - OneShot.inject(OSM(LeftControl), keyState); +void macroToggleOneShot() { + OneShot.toggleAutoOneShot(); } const macro_t *macroAction(uint8_t macroIndex, uint8_t keyState) { - if (macroIndex == OSMALTCTRL) { - macroOneShotAltControl(keyState); + if (macroIndex == TOGGLE_ONESHOT) { + macroToggleOneShot(); } return MACRO_NONE; diff --git a/plugins/Kaleidoscope-LED-ActiveModColor/README.md b/plugins/Kaleidoscope-LED-ActiveModColor/README.md index 16dc6ba2..e2a2c709 100644 --- a/plugins/Kaleidoscope-LED-ActiveModColor/README.md +++ b/plugins/Kaleidoscope-LED-ActiveModColor/README.md @@ -2,9 +2,8 @@ With this plugin, any active modifier on the keyboard will have the LED under it highlighted. No matter how the modifier got activated (a key press, a macro, -anything else), the coloring will apply. Layer keys, be them layer toggles, -momentary switches, or one-shot layer keys count as modifiers as far as the -plugin is concerned. +anything else), the coloring will apply. Layer shift keys and OneShot layer keys +count as modifiers as far as the plugin is concerned. ## Using the plugin @@ -37,11 +36,20 @@ properties: ### `.highlight_color` -> The color to use for highlighting the modifiers. Defaults to a white color. +> The color to use for highlighting normal modifier keys and +> layer-shift keys. Defaults to a white color. + +### `.oneshot_color` + +> The color to use for highlighting active one-shot keys. These are +> the keys that will time out or deactivate when a subsequent key is +> pressed. Defaults to a yellow color. ### `.sticky_color` -> The color to use for highlighting one-shot modifiers when they are sticky. Defaults to a red color. +> The color to use for highlighting "sticky" one-shot keys. These keys +> will remain active until they are pressed again. Defaults to a red +> color. ## Plugin methods diff --git a/plugins/Kaleidoscope-OneShot/README.md b/plugins/Kaleidoscope-OneShot/README.md index 539b997e..f0e8699d 100644 --- a/plugins/Kaleidoscope-OneShot/README.md +++ b/plugins/Kaleidoscope-OneShot/README.md @@ -23,7 +23,7 @@ active if another one-shot of the same type is tapped, so `Ctrl, Alt, b` becomes `Ctrl+Alt+b`, and `L1, L2, c` is turned into `L1+L2+c`. Furthermore, modifiers and other layer keys do not cancel the one-shot effect, either. -## Using One-Shot Keys +## Using One-Shot keys To enter one-shot mode, tap _quickly_ on a one-shot key. The next normal (non-one-shot) key you press will have the modifier applied, @@ -40,11 +40,30 @@ modifier will now stay on until you press it again. Continuing the Shift, d, e, f` will give you `ABCdef`. This can be a bit tricky; combining this plugin with -[LED-ActiveModColor](Kaleidoscope-LED-ActiveModColor.md) -will help you understand what state your one-shot is in; when a -one-shot key is active, it will have a white LED highlight; when -sticky, a red highlight. (These colors are configurable.) +[LED-ActiveModColor](Kaleidoscope-LED-ActiveModColor.md) will help you +understand what state your one-shot is in; when a one-shot key is active, it +will have a yellow LED highlight; when sticky, a red highlight. When it is in a +"held" state, but will be deactivated when released like any non-one-shot key, +it will have a white highlight. (These colors are configurable.) +## Special OneShot keys + +OneShot also comes with two special keys that can make any key on your keyboard +sticky: `OneShot_MetaStickyKey` & `OneShot_ActiveStickyKey`. These are both +`Key` values that can be used as entries in your sketch's keymap. + +### `OneShot_MetaStickyKey` + +This special OneShot key behaves like other OneShot keys, but its affect is to +make the next key pressed sticky. Tap `OneShot_MetaStickyKey`, then tap `X`, and +`X` will become sticky. Tap `X` again to deactivate it. + +### `OneShot_ActiveStickyKey` + +This special key doesn't act like a OneShot key, but instead makes any key(s) +currently held (or otherwise active) sticky. Press (and hold) `X`, tap +`OneShot_ActiveStickyKey`, then release `X`, and `X` will stay active until it +is tapped again to deactivate it. ## Using the plugin @@ -69,13 +88,13 @@ void setup() { There are two macros the plugin provides: -### `OSM(mod)` +#### `OSM(mod)` > A macro that takes a single argument, the name of the modifier: `LeftControl`, > `LeftShift`, `LeftAlt`, `LeftGui` or their right-side variant. When marked up > with this macro, the modifier will act as a one-shot modifier. -### `OSL(layer)` +#### `OSL(layer)` > Takes a layer number as argument, and sets up the key to act as a one-shot > layer key. @@ -83,85 +102,203 @@ There are two macros the plugin provides: > Please note that while `Kaleidoscope` supports more, one-shot layers are > limited to 8 layers only. +In addition, there is a special key: + +#### `Key_MetaSticky` + +> A key that behaves like a one-shot key, but while active, it makes +> other keys that are pressed become sticky, just like double-tapped +> one-shot keys. + ## Plugin methods The plugin provides one object, `OneShot`, which implements both one-shot modifiers and one-shot layer keys. It has the following methods: -### `.isActive()` +### Configuration methods: Timeouts -> Returns if any one-shot key is in flight. This makes it possible to -> differentiate between having a modifier or layer active, versus having them -> active only until after the next key getting pressed. And this, in turn, is -> useful for macros that need to fiddle with either modifier or layer state: if -> one-shots are not active, they need not restore the original state. +#### `.setTimeout(timeout)` -### `.isPressed()` +> OneShot keys will remain active after they're pressed for `timeout` +> milliseconds (or until a subsequent non-oneshot key is pressed). The +> default value is 2500 (2.5 seconds). -> Returns true if any one-shot key is still held. +#### `.setHoldTimeout(timeout)` -### `.isSticky(key)` +> If a one-shot key is held for longer than `timeout` milliseconds, it +> will behave like a normal key, and won't remain active after it is +> released. The default value is 250 (1/4 seconds). -> Returns if the key is currently sticky. +#### `.setDoubleTapTimeout(timeout)` -### `.isModifierActive(key)` +> If a one-shot key is double-tapped (pressed twice in a row) in less +> than `timeout` milliseconds, it wil become sticky, and will remain +> active until it is pressed a third time. The default value is -1, +> which indicates that it should use the same timeout as +> `.setTimeout()`. -> Returns if the modifier `key` has a one-shot state active. Use this together -> with `Kaleidoscope.hid().keyboard().isModifierKeyActive` to catch cases where -> a one-shot modifier is active, but not registered yet. +### Configuration methods: Stickability -### `.cancel([with_stickies])` +#### `.enableStickability(key...)` +#### `.disableStickability(key...)` -> The `cancel()` method can be used to cancel any pending one-shot effects, -> useful when one changed their minds, and does not wish to wait for the -> timeout. +> Enables/disables stickability for all keys listed. The keys should +> all be OneShot keys, modifier keys, or layer-shift keys, as +> specified on the keymap. For example: +> `OneShot.enableStickability(OSM(LeftShift), OSL(1), Key_RightGUI)`. +> `OneShot.disableStickability(OSM(RighttAlt), OSL(2), ShiftToLayer(4))`. > -> The optional `with_stickies` argument, if set to `true`, will also cancel -> sticky one-shot effects. If omitted, it defaults to `false`, and not canceling -> stickies. +> By default, all OneShot keys are stickable. -### `.inject(key, keyState)` +#### `.enableStickabilityForModifiers()` +#### `.enableStickabilityForLayers()` +#### `.disableStickabilityForModifiers()` +#### `.disableStickabilityForLayers()` -> Simulates a key event, specifically designed to inject one-shot keys into the -> event loop. The primary purpose of this method is to make it easier to trigger -> multiple one-shots at the same time. -> -> See the example sketch for more information about its use. +> Enables/disables stickability for all modifiers and layers, +> respectively. These are convenience methods for cases where one +> wants to enable stickability for a group of one-shot keys. -### `.enableStickability(key...)` +### Configuration methods: Automatic one-shot keys -> Enables stickability for all keys listed. The keys should all be OneShot keys, -> as if specified on the keymap. For example: -> `OneShot.enableStickability(OSM(LeftShift), OSL(1))`. -> -> By default, all oneshot keys are stickable. +#### `.enableAutoModifiers()` +#### `.disableAutoModifiers()` +#### `.toggleAutoModifiers()` -### `.enableStickabilityForModifiers()` -### `.enableStickabilityForLayers()` +> Enables/disables/toggles auto-oneshot functionality for modifier +> keys. When enabled, all normal modifier keys, including those with +> other modifier flags added to them (e.g. `LSHIFT(Key_LeftAlt)`, +> `Key_Meh`) will be automatically treated as one-shot keys, in +> addition to dedicated ones like `OSM(LeftGui)`. -> Enables stickability for all modifiers and layers, respectively. These are -> convenience methods for cases where one wants to enable stickability for a -> group of one-shot keys. +#### `.enableAutoLayers()` +#### `.disableAutoLayers()` +#### `.toggleAutoLayers()` -### `.disableStickability(key...)` +> Enables/disables/toggles auto-oneshot functionality for layer shift +> keys (see above). -> Disables stickability for all keys listed. The keys should all be OneShot keys, -> as if specified on the keymap. For example: -> `OneShot.disableStickability(OSM(LeftShift), OSL(1))`. -> -> By default, all oneshot keys are stickable. +#### `.enableAutoOneShot()` +#### `.disableAutoOneShot()` +#### `.toggleAutoOneShot()` + +> Enables/disables/toggles auto-oneshot functionality for all +> modifiers and layer shift keys. + +### Test methods + +#### `.isActive(key_addr)` + +> Returns `true` if the key at `key_addr` is in an active one-shot +> state. Note that if a key is still being held, but will be not +> remain active after it is released, it is not considered to be in a +> one-shot state, even if it had been earlier. + +#### `.isTemporary(key_addr)` + +> Returns `true` if the key at `key_addr` is in a temporary one-shot +> state. Such a key will eventually time out or get deactivated by a +> subsequent key press. + +#### `.isSticky(key_addr)` + +> Returns `true` if the key at `key_addr` is in a permanent one-shot +> state. Such a key will not be deactivated by subsequent keypresses, +> nor will it time out. It will only be deactivated by pressing it one +> more time, or by being cancelled by the `cancel()` method (see +> below). + +#### `.isActive()` + +> Returns `true` if there are any active one-shot keys. Note: it +> returns `false` if there are no currently active one-shot keys, but +> there are keys that were at one time in a one-shot state, but are +> still being held after that state has been cancelled. + +#### `.isSticky()` + +> Returns `true` if there are any sticky one-shot keys. + +#### `.isStickable(key)` + +> Returns `true` if a key of the specified value can be made sticky by +> double-tapping. + +#### `.isModifier(key)` + +> Returns `true` if the specified key is a modifier key. This does not +> include OneShot modifiers (e.g. `OSM(LeftShift)`), but it does +> include modifiers with additional modifier flags (e.g. `Key_Meh`, +> `LCTRL(Key_RightGui)`). + +#### `.isLayerShift(key)` + +> Returns `true` if the specified key is a layer-shift key +> (e.g. `ShiftToLayer(2)`). OneShot layer keys (e.g. `OSL(5)` are not +> included). + +#### `.isOneShotKey(key)` + +> Returns `true` if the specified key is a OneShot modifier or +> layer-shift key (e.g. `OSM(LeftAlt)`, `OSL(3)`). + +### Other methods + +#### `.cancel([with_stickies])` + +> Immediately deactivates the one-shot status of any _temporary_ +> one-shot keys. Any keys still being physically held will continue to +> function as normal modifier/layer-shift keys. +> +> If `with_stickies` is `true` (the default is `false`), _sticky_ +> one-shot keys will also be deactivated, in the same way. + +### Deprecated methods + +The following methods have been deprecated, and should no longer be +used, if possible. These functions made more sense when OneShot was +based on `Key` values; it has since be rewritten to be based on +`KeyAddr` values. + +#### `.inject(key, key_state)` + +> Finds an idle key on the keyboard, and turns it into a one-shot +> key. When OneShot was based on `Key` values, this made more sense, +> as it didn't need a valid `KeyAddr` to work. Since the main purpose +> of this method was to enable the triggering of multiple one-shot +> modifiers with a single key, it is much better to use automatic +> one-shot modifiers, if possible, because then it's not necessary to +> use a Macro to configure. + +#### `.isModifierActive(key)` + +> Returns `true` if a keymap cache entry with the current value of +> `key` is active (one-shot, sticky, or held). This should be a +> function that is not specific to OneShot. + +#### `.isActive(key)` + +> Returns `true` if a keymap cache entry with the current value of +> `key` is in an active one-shot state. Please use +> `.isActive(key_addr)` instead. + +#### `.isSticky(key)` + +> Returns `true` if a keymap cache entry with the current value of +> `key` is in a sticky one-shot state. Please use +> `.isSticky(key_addr)` instead. -### `.disableStickabilityForModifiers()` -### `.disableStickabilityForLayers()` +#### `.isPressed()` -> Disables stickability for all modifiers and layers, respectively. These are -> convenience methods for cases where one wants to disable stickability for a -> group of one-shot keys. +> Returns `false`. OneShot doesn't need to keep track of whether or +> not a one-shot key is still pressed any more. This function was +> mainly used by LED-ActiveModColor, which no longer needs it. -## Plugin properties +## Plugin properties **[DEPRECATED]** -Along with the methods listed above, the `OneShot` object has the following -properties too: +Along with the methods listed above, the `OneShot` object has the +following properties, too. [Note: these have all been deprecated, +please use the `.set*Timeout()` methods above instead.] ### `.time_out`