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 <algernon@keyboard.io>
pull/447/head
Gergely Nagy 6 years ago
parent 4fb60c0a7c
commit 7ccb5bfa63
No known key found for this signature in database
GPG Key ID: AC1E90BAC433F68F

@ -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. 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 ## New plugins

@ -16,6 +16,7 @@ If any of this does not make sense to you, or you have trouble updating your .in
- [MagicCombo](#magiccombo) - [MagicCombo](#magiccombo)
- [TypingBreaks](#typingbreaks) - [TypingBreaks](#typingbreaks)
+ [Deprecated APIs and their replacements](#deprecated-apis-and-their-replacements) + [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) - [Source code and namespace rearrangement](#source-code-and-namespace-rearrangement)
* [Removed APIs](#removed-apis) * [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 ## 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 ### 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**. 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**.

@ -2,17 +2,21 @@
One-shots are a new kind of behaviour for your standard modifier and momentary 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 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 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 advantage is that this allows us to place the modifiers and layer keys to
positions that would otherwise be awkward when chording. Nevertheless, they positions that would otherwise be awkward when chording. Nevertheless, they
still act as normal when held, that behaviour is not lost. 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 Furthermore, if a one-shot key is double-tapped ie tapped two times in quick
becomes sticky, and remains active until disabled with a third tap. This can be succession, it becomes sticky, and remains active until disabled with a third tap.
useful when one needs to input a number of keys with the modifier or layer This can be useful when one needs to input a number of keys with the modifier or
active, and still does not wish to hold the key down. If this feature is layer active, and does not wish to hold the key down. If this "stickability"
undesirable, unset the `OneShot.double_tap_sticky` property (see later). 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 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 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) [LED-ActiveModColor](LED-ActiveModColor.md)
will help you understand what state your one-shot is in; when a 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 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 ## 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. > 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 ## Plugin properties
Along with the methods listed above, the `OneShot` object has the following Along with the methods listed above, the `OneShot` object has the following
@ -148,22 +182,6 @@ properties too:
> >
> Defaults to 200. > 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` ### `.double_tap_time_out`
> Set this property to the number of milliseconds within which a second > Set this property to the number of milliseconds within which a second

@ -26,15 +26,17 @@ uint32_t OneShot::start_time_ = 0;
uint16_t OneShot::time_out = 2500; uint16_t OneShot::time_out = 2500;
uint16_t OneShot::hold_time_out = 250; uint16_t OneShot::hold_time_out = 250;
int16_t OneShot::double_tap_time_out = -1; 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 = true;
bool OneShot::double_tap_sticky_layers = 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_; Key OneShot::prev_key_;
bool OneShot::should_cancel_ = false; bool OneShot::should_cancel_ = false;
bool OneShot::should_cancel_stickies_ = false; bool OneShot::should_cancel_stickies_ = false;
uint8_t OneShot::positions_[16]; uint8_t OneShot::positions_[16];
bool OneShot::use_new_stickies_ = false;
// --- helper macros ------ // --- helper macros ------
@ -59,6 +61,18 @@ uint8_t OneShot::positions_[16];
#define hasTimedOut() (millis () - start_time_ >= time_out) #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) { void OneShot::positionToCoords(uint8_t pos, byte *row, byte *col) {
*col = pos % COLS; *col = pos % COLS;
*row = (pos - *col) / COLS; *row = (pos - *col) / COLS;
@ -136,15 +150,7 @@ EventHandlerResult OneShot::onKeyswitchEvent(Key &mapped_key, byte row, byte col
if (keyToggledOn(keyState)) { if (keyToggledOn(keyState)) {
setPressed(idx); setPressed(idx);
bool set_sticky = false; if (isSameAsPrevious(mapped_key) && isStickable(mapped_key)) {
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 ((millis() - start_time_) <= ((double_tap_time_out == -1) ? time_out : double_tap_time_out)) { if ((millis() - start_time_) <= ((double_tap_time_out == -1) ? time_out : double_tap_time_out)) {
setSticky(idx); setSticky(idx);
@ -257,6 +263,40 @@ void OneShot::cancel(bool with_stickies) {
should_cancel_stickies_ = 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;
}
} }
} }

@ -46,6 +46,29 @@ class OneShot : public kaleidoscope::Plugin {
static uint16_t time_out; static uint16_t time_out;
static int16_t double_tap_time_out; static int16_t double_tap_time_out;
static uint16_t hold_time_out; static uint16_t hold_time_out;
static inline void enableStickablity() {}
static void enableStickability(Key key);
template <typename... Keys>
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 <typename... Keys>
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;
static bool double_tap_sticky_layers; static bool double_tap_sticky_layers;
@ -68,11 +91,13 @@ class OneShot : public kaleidoscope::Plugin {
static uint32_t start_time_; static uint32_t start_time_;
static state_t state_; static state_t state_;
static state_t sticky_state_; static state_t sticky_state_;
static state_t stickable_state_;
static state_t pressed_state_; static state_t pressed_state_;
static Key prev_key_; static Key prev_key_;
static bool should_cancel_; static bool should_cancel_;
static bool should_cancel_stickies_; static bool should_cancel_stickies_;
static uint8_t positions_[16]; static uint8_t positions_[16];
static bool use_new_stickies_;
static void positionToCoords(uint8_t pos, byte *row, byte *col); static void positionToCoords(uint8_t pos, byte *row, byte *col);

Loading…
Cancel
Save