From 7ccb5bfa635508c5d305d422e138c930576c9dfa Mon Sep 17 00:00:00 2001 From: Gergely Nagy Date: Sat, 27 Oct 2018 11:17:09 +0200 Subject: [PATCH] OneShot: Implement finer stickability controls Based on the work of Shriramana Sharma (@jamadagni) in keyboardio/Kaleidoscope-OneShot#45, this implements finer stickability controls for `OneShot`, allowing one to set stickability on a per-key basis. The old `.double_tap_sticky` and `.double_tap_sticky_layers` properties still work, but are deprecated. Signed-off-by: Gergely Nagy --- NEWS.md | 6 ++- UPGRADING.md | 7 ++++ doc/plugin/OneShot.md | 64 ++++++++++++++++++----------- src/kaleidoscope/plugin/OneShot.cpp | 64 +++++++++++++++++++++++------ src/kaleidoscope/plugin/OneShot.h | 25 +++++++++++ 5 files changed, 130 insertions(+), 36 deletions(-) diff --git a/NEWS.md b/NEWS.md index f04ee935..2ac9d17d 100644 --- a/NEWS.md +++ b/NEWS.md @@ -60,7 +60,11 @@ It is now possible to detach, and re-attach the USB link from/to the host, witho See the [Kaleidoscope-USB-Quirks][plugin:USB-Quirks] plugin for a use-case. - [plugin:USB-Quirks]: https://github.com/keyboardio/Kaleidoscope-USB-Quirks + [plugin:USB-Quirks]: hAttps://github.com/keyboardio/Kaleidoscope-USB-Quirks + +### Finer stickability controls for OneShot + +The [OneShot plugin](doc/plugin/OneShot.md) gained finer stickability controls, one can now control whether the double-tap stickiness is enabled on a per-key basis. See [UPGRADING.md](UPGRADING.md#finer-oneshot-stickability-control) for more information. ## New plugins diff --git a/UPGRADING.md b/UPGRADING.md index 78316787..d65d43a1 100644 --- a/UPGRADING.md +++ b/UPGRADING.md @@ -16,6 +16,7 @@ If any of this does not make sense to you, or you have trouble updating your .in - [MagicCombo](#magiccombo) - [TypingBreaks](#typingbreaks) + [Deprecated APIs and their replacements](#deprecated-apis-and-their-replacements) + - [Finer OneShot stickability control](#finer-oneshot-stickability-control) - [Source code and namespace rearrangement](#source-code-and-namespace-rearrangement) * [Removed APIs](#removed-apis) @@ -433,6 +434,12 @@ Storing the settable settings in EEPROM makes it depend on `Kaleidoscope-EEPROM- ## Deprecated APIs and their replacements +### Finer OneShot stickability control + +The [OneShot plugin](doc/plugin/OneShot.md) has much improved stickability control. Instead of only being able to control if one-shot layers should be stickable too, or disabling the sticky feature in general, it is now possible to control stickiness on a per-key basis with the new `OneShot.enableStickability()` and `OneShot.disableStickablity()` methods. + +The old method of setting `OneShot.double_tap_sticky` or `OneShot.double_tap_layer_sticky` continue to work, but using them is deprecated, and as such, these will be removed by **2019-01-14**. + ### Source code and namespace rearrangement With the move towards a monorepo-based source, some headers have moved to a new location, and plenty of plugins moved to a new namespace (`kaleidoscope::plugin`). This means that the old headers, and some old names are deprecated. Unless noted otherwise, the old names still work, but will likely emit a warning, and the compatibility shims will be removed by **2019-01-14**. diff --git a/doc/plugin/OneShot.md b/doc/plugin/OneShot.md index 26d61fe5..d04ab1b6 100644 --- a/doc/plugin/OneShot.md +++ b/doc/plugin/OneShot.md @@ -2,17 +2,21 @@ One-shots are a new kind of behaviour for your standard modifier and momentary layer keys: instead of having to hold them while pressing other keys, they can -be tapped and released, and will remain active until any other key is pressed. +be tapped and released, and will remain active until any other key is pressed +subject to a time-out. + In short, they turn `Shift, A` into `Shift+A`, and `Fn, 1` to `Fn+1`. The main advantage is that this allows us to place the modifiers and layer keys to positions that would otherwise be awkward when chording. Nevertheless, they still act as normal when held, that behaviour is not lost. -Furthermore, if a one-shot key is tapped two times in quick succession, it -becomes sticky, and remains active until disabled with a third tap. This can be -useful when one needs to input a number of keys with the modifier or layer -active, and still does not wish to hold the key down. If this feature is -undesirable, unset the `OneShot.double_tap_sticky` property (see later). +Furthermore, if a one-shot key is double-tapped ie tapped two times in quick +succession, it becomes sticky, and remains active until disabled with a third tap. +This can be useful when one needs to input a number of keys with the modifier or +layer active, and does not wish to hold the key down. If this "stickability" +feature is undesirable, it can be unset (and later again set) for individual +modifiers/layers. If stickability is unset, double-tapping a one-shot modifier +will just restart the timer. To make multi-modifier, or multi-layer shortcuts possible, one-shot keys remain active if another one-shot of the same type is tapped, so `Ctrl, Alt, b` becomes @@ -39,7 +43,7 @@ This can be a bit tricky; combining this plugin with [LED-ActiveModColor](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. +sticky, a red highlight. (These colors are configurable.) ## Using the plugin @@ -125,6 +129,36 @@ modifiers and one-shot layer keys. It has the following methods: > > See the example sketch for more information about its use. +### `.enableStickability(key...)` + +> 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. + +### `.enableStickabilityForModifiers()` +### `.enableStickabilityForLayers()` + +> 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. + +### `.disableStickability(key...)` + +> 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. + +### `.disableStickabilityForModifiers()` +### `.disableStickabilityForLayers()` + +> 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. + ## Plugin properties Along with the methods listed above, the `OneShot` object has the following @@ -148,22 +182,6 @@ properties too: > > Defaults to 200. -### `.double_tap_sticky` - -> Set this boolean property to make the plugin treat a double-tap of a one-shot -> key as making it sticky until a third tap. Setting it to `false` disables this -> behaviour, in which case double-tapping a one-shot modifier will just restart -> the timer. -> -> Defaults to `true`. - -### `.double_tap_sticky_layers` - -> The same as `.double_tap_sticky`, but only applies to layers. The two can be -> set separately. -> -> Defaults to `true`. - ### `.double_tap_time_out` > Set this property to the number of milliseconds within which a second diff --git a/src/kaleidoscope/plugin/OneShot.cpp b/src/kaleidoscope/plugin/OneShot.cpp index 803861cf..8a432b0b 100644 --- a/src/kaleidoscope/plugin/OneShot.cpp +++ b/src/kaleidoscope/plugin/OneShot.cpp @@ -26,15 +26,17 @@ uint32_t OneShot::start_time_ = 0; uint16_t OneShot::time_out = 2500; uint16_t OneShot::hold_time_out = 250; int16_t OneShot::double_tap_time_out = -1; +OneShot::state_t OneShot::state_ = {0, 0}; +OneShot::state_t OneShot::sticky_state_ = {0, 0}; +OneShot::state_t OneShot::stickable_state_ = {0xFF, 0xFF}; +OneShot::state_t OneShot::pressed_state_ = {0, 0}; bool OneShot::double_tap_sticky = true; bool OneShot::double_tap_sticky_layers = true; -OneShot::state_t OneShot::state_; -OneShot::state_t OneShot::sticky_state_; -OneShot::state_t OneShot::pressed_state_; Key OneShot::prev_key_; bool OneShot::should_cancel_ = false; bool OneShot::should_cancel_stickies_ = false; uint8_t OneShot::positions_[16]; +bool OneShot::use_new_stickies_ = false; // --- helper macros ------ @@ -59,6 +61,18 @@ uint8_t OneShot::positions_[16]; #define hasTimedOut() (millis () - start_time_ >= time_out) +bool OneShot::isStickable(Key key) { + if (use_new_stickies_) + return bitRead(stickable_state_.all, key.raw - ranges::OS_FIRST); + + if (key >= ranges::OSM_FIRST && key <= ranges::OSM_LAST) + return true; + else if (key >= ranges::OSL_FIRST && key <= ranges::OSL_LAST) + return double_tap_sticky_layers; + + return false; +} + void OneShot::positionToCoords(uint8_t pos, byte *row, byte *col) { *col = pos % COLS; *row = (pos - *col) / COLS; @@ -136,15 +150,7 @@ EventHandlerResult OneShot::onKeyswitchEvent(Key &mapped_key, byte row, byte col if (keyToggledOn(keyState)) { setPressed(idx); - bool set_sticky = false; - if (isSameAsPrevious(mapped_key)) { - if (mapped_key >= ranges::OSM_FIRST && mapped_key <= ranges::OSM_LAST && double_tap_sticky) - set_sticky = true; - else if (mapped_key >= ranges::OSL_FIRST && mapped_key <= ranges::OSL_LAST && double_tap_sticky_layers) - set_sticky = true; - } - - if (set_sticky) { + if (isSameAsPrevious(mapped_key) && isStickable(mapped_key)) { if ((millis() - start_time_) <= ((double_tap_time_out == -1) ? time_out : double_tap_time_out)) { setSticky(idx); @@ -257,6 +263,40 @@ void OneShot::cancel(bool with_stickies) { should_cancel_stickies_ = with_stickies; } +void OneShot::enableStickability(Key key) { + use_new_stickies_ = true; + + if (key >= ranges::OS_FIRST && key <= ranges::OS_LAST) + bitSet(stickable_state_.all, (key.raw - ranges::OS_FIRST)); +} + +void OneShot::disableStickability(Key key) { + use_new_stickies_ = true; + + if (key >= ranges::OS_FIRST && key <= ranges::OS_LAST) + bitClear(stickable_state_.all, (key.raw - ranges::OS_FIRST)); +} + +void OneShot::enableStickabilityForModifiers() { + use_new_stickies_ = true; + stickable_state_.mods = 0xFF; +} + +void OneShot::enableStickabilityForLayers() { + use_new_stickies_ = true; + stickable_state_.layers = 0xFF; +} + +void OneShot::disableStickabilityForModifiers() { + use_new_stickies_ = true; + stickable_state_.mods = 0; +} + +void OneShot::disableStickabilityForLayers() { + use_new_stickies_ = true; + stickable_state_.layers = 0; +} + } } diff --git a/src/kaleidoscope/plugin/OneShot.h b/src/kaleidoscope/plugin/OneShot.h index a8797d23..42cee2dc 100644 --- a/src/kaleidoscope/plugin/OneShot.h +++ b/src/kaleidoscope/plugin/OneShot.h @@ -46,6 +46,29 @@ class OneShot : public kaleidoscope::Plugin { static uint16_t time_out; static int16_t double_tap_time_out; static uint16_t hold_time_out; + + static inline void enableStickablity() {} + static void enableStickability(Key key); + template + static void enableStickability(Key key, Keys&&... keys) { + enableStickability(key); + enableStickability(keys...); + } + static void enableStickabilityForModifiers(); + static void enableStickabilityForLayers(); + + static inline void disableStickability() {} + static void disableStickability(Key key); + template + static void disableStickability(Key key, Keys&&... keys) { + disableStickability(key); + disableStickability(keys...); + } + static void disableStickabilityForModifiers(); + static void disableStickabilityForLayers(); + + static bool isStickable(Key key); + static bool double_tap_sticky; static bool double_tap_sticky_layers; @@ -68,11 +91,13 @@ class OneShot : public kaleidoscope::Plugin { static uint32_t start_time_; static state_t state_; static state_t sticky_state_; + static state_t stickable_state_; static state_t pressed_state_; static Key prev_key_; static bool should_cancel_; static bool should_cancel_stickies_; static uint8_t positions_[16]; + static bool use_new_stickies_; static void positionToCoords(uint8_t pos, byte *row, byte *col);