Kaleidoscope
Kaleidoscope copied to clipboard
Feature request: adaptive keys
Is your feature request related to a problem? Please describe. Most keyboard layouts have a few common bigrams that are just uncomfortable to type, often because two adjacent letters use the same finger. For example, in the Colemak-DH layout, typing the word "true" takes two consecutive presses of my right middle finger, on the upper row followed by the home row.
Describe the solution you'd like "Adaptive keys" are a way to address these bigrams without completely changing the keyboard layout. The idea is that a letter key could send different letters, depending on what was pressed beforehand. For example, on the QWERTY layout, you could define W as an adaptive key, and add the sequence Q-W -> QU. This would mean that pressing Q-W within a certain window of time would actually send the QU keypresses.
In my "true" example, I have another keyboard where I've set Y as an adaptive key such that U-Y -> UE. In my layout, Y is the ring finger key in the upper row, so what used to be a same-finger bigram is now a roll from middle finger to ring finger in the upper row. UY is much less common in English, but not unused, so when I do need to type a literal U-Y, I just delay the press of the Y a little bit.
Some people may want the ability for an adaptive key to modify the first keystroke, not just the second one. A workaround for this could be "send backspace, then the two keycodes," but a more intelligent solution might be to make the first key the trigger instead of the second one, holding off on sending any key events until it knows what the next keypress will be (or until the timer expires).
Describe alternatives you've considered Adaptive keys aren't macros, since they require multiple keypresses. They also aren't combos since the keypresses will be in sequence.
The closest example I can find in the existing Kaleidoscope codebase is the leader key. It might be possible to adapt a leader key to be an adaptive key, but it would need an "else" or a "default" case if the following keys weren't recognized. I also think defining it this way would make for confusing code.
Additional context If the feature is implemented, there should be a callback exposed to turn the behavior on and off. This will allow users to disable it for things like gaming.
One of the big examples of the use of Adaptive Keys is the Hands Down keyboard layout page.
This would be a plugin with a lot of similarity to Qukeys, especially if it could change the value of the first key of the bigram (or sequence, if it gets extended to more than just two keys). And it would be yet another plugin that's sensitive to the timing of physical events, which can get distorted by other plugins that also delay events (e.g. Qukeys). I think it might be time to once again consider a global event queue that stores events with their original timestamps so that plugins can more reliably respond to user input when other plugins are also involved in processing that input.
Here's an outline for a design for "adaptive keys" (I'm not thrilled about that name because it's not very descriptive of the functionality, but I haven't got a better alternative yet):
It stores a table of Key
value input pairs, which are decoded to output Key
pairs. When it receives a key toggle-on event, it checks event.key
against the first item in each entry. On a match, it queues that event and waits for the next key toggle-on (or a timeout). If a key toggles on while there is still a keypress event in the queue, and both match a given entry in the table, it changes both event.key
values and flushes the queue. Key_NoKey
can be used as a signal to suppress the event.
The event queue will need to store more than just the two toggle-on events, because other keys could toggle off in the interim, and we don't want to re-order those events (e.g. a modifier toggles off between the two press events).
I haven't had time to consider all of the possible corner cases yet, but I think it ends up being a fairly straightforward plugin. As I already mentioned, this is spurring me to resurrect the idea of a global shared event queue for both efficiency and functionality, but it should be fairly simple to convert this plugin to that core design at a later date.