rvaiya/keyd

Oneshot with timeout and fallback

Opened this issue ยท 4 comments

I would like to propose a variant of the oneshot action with a timeout and a fallback or default action/key. For example:

capslock = oneshot(shift, 500, escape)

I am aware of the global setting oneshot_timeout introduces as part of #277, but in the use case I have in mind, and action should take place after the timeout. Consider the following behaviour for the capslock key:

  • Tap -> CapsLock
  • Hold -> Layer 1
  • Tap then hold -> Layer 2

My first attempt to achieve this looked similar to this

[main]

capslock = overload(layer1, timeout(oneshot(switch), 500, capslock))

[switch]

capslock = overload(layer2, capslock)

However, this does not seem to produce the expected result: tapping capslock the first time does eventually turn capslock mode on, but then it won't turn off after the next tap (possibly because oneshot is still active after the timeout).

Assuming this is not the intended use of timeout, what I would expect to be possible is the following:

[main]

capslock = overload(layer1, oneshot(switch, 500, capslock))

[switch]

capslock = overload(layer2, capslock)

Using the existing oneshot I can achieve what I want except capslock is only triggered by double-tapping, which is not ideal. Apologies again if what I want to achieve is already possible and I just haven't been able to figure it out.

If I understand correctly, the problem is that you want to emit capslock on tap, except if you press it again within some time. To disambiguate, we'd have to wait and see if the timeout expires or another non-capslock key is struck.

I'm not convinced that an action at the end of the oneshot timeout is generally useful. Are there other use cases?

Would a solution that just immediately emits capslock on tap, and cancels it by emitting capslock again, be acceptable? Ie:

[global]
oneshot_timeout = 500

[main]
capslock = overload(layer1, oneshotm(switch, capslock))

[switch]
capslock = layerm(layer2, capslock)

As an aside: timeout(โ€ฆ) refers to the hold time of a key, it is not a general idle time expiration like oneshot_timeout is.

See also #671/#711 for related timeout trickery.

Thanks @nsbgn for quick response and useful suggestion. I think your approach may suffice in my particular at this stage.

I suppose that if I wanted capslock to behave like escape or backspace, like Vim and Colemak users like do, there wouldn't be an easy way to disable them. Tab is another key that I would be willing to wait for a fraction of a second before it is executed if that means I can add tap-hold capabilities to it. I would personally not use the proposed variant of oneshot for printable characters on the main layer. The aforementioned delay would be unbearable when typing.

But when you asked for other use cases, what came to my mind was how we used to type on the num-pads of our mobile phones before the iPhone revolution (I didn't have a BlackBerry ๐Ÿ˜†). You would tap each key multiple times to get the character you wanted and it would print:

  1. after a short period of idleness or
  2. if you pressed another character

What I am proposing does not suggest we support the latter (2). As far as I understand, the current implementation of oneshot will consider the shot "taken" when any key is pressed, within the timeout, regardless of whether it is specified in the selected layer or not (please correct me if I am wrong). That's what I expect and that's why I called it a variant of oneshot.

I would not want to replicate the dumb-phone typing experience exactly, but it could be useful for having a symbols layer which allows you to print different symbols depending on the number of taps. The same approach could be used for some foreign languages that have multiple variants of the same letter.

An alternative would be to focus on the tap-hold or multi-tap gestures specifically, but I assume that would require some form of disambiguation to discard key presses that don't match the key that triggered the action.

I am inclined to agree with @nsbgn, using time exclusively to disambiguate intent is a bad idea (versions of this idea have been discussed many times before).

While it may seem desirable to have <capslock> <a> and <capslock> <500ms> <a> do different things, in practice the timeout either has to be long enough to make the second use case cumbersome, or short enough to make the first hard to achieve deliberately.

In this case I think you are trying to make capslock do too much work. In the example config you gave you can already activate capslock by double tapping it, so allowing it to be activated after a single tap and then a timeout seems superfluous and introduces unnecessary ambiguity.

But when you asked for other use cases, what came to my mind was how we used to type on the num-pads of our mobile phones before the iPhone revolution (I didn't have a BlackBerry ๐Ÿ˜†). You would tap each key multiple times to get the character you wanted and it would print:

  1. after a short period of idleness or
  2. if you pressed another character

Something like this could be achieved using the proposed togglet action in #671, which also has other uses and is more like to be implemented. It seems you desire a particular variant of it which emits a key on cancellation and seems less generally useful.

Edit: Sorry, to implement the feature you describe the action would indeed need to be capable of performing an action on timeout expiry. Having said that, I remain unconvinced of its practical utility.

Thanks for your feedback @rvaiya.

versions of this idea have been discussed many times before

I am sure this is the case and I will try to find the time to read more of the history of issues that have been raised in the past to understand why Keyd works as it does today.

While it may seem desirable to have and <500ms> do different things, in practice the timeout either has to be long enough to make the second use case cumbersome, or short enough to make the first hard to achieve deliberately.

The problem you describe has probably been the same since the invention of the double click. I am not as original as I'd like to be. What I am trying to achieve, and the challenges involved, could not have been explained any clearer by (the apparently famous) Ben Vallack in his video.

In this case I think you are trying to make capslock do too much work.

I am trying to extract as much value as possible from the CapsLock key, I won't deny that. The concept I am trying out is to have the CapsLock key being the entry and exit point of my layers (extend and numpad in my case) so I always know where I am.

It seems you desire a particular variant of it which emits a key on cancellation and seems less generally useful.

Referring to the video I shared above, I would not say emitting a key on cancellation is useful, but is apparently a necessary tradeoff to achieve tap-hold/double-tap. As Vallack, I do see potential in this capability.

Edit: Sorry, to implement the feature you describe the action would indeed need to be capable of performing an action on timeout expiry. Having said that, I remain unconvinced of its practical utility.

I understand if this is not the direction Keyd wants to take. @nsbgn's suggestion will allow me to test-drive the concept for the moment to better understand its practicality. I noticed your main competitors seem to support what I propose already, according to their README. I may have to dust my old config file and try it out, but I have left them for Keyd in the past because I much prefer the simplicity of use and configuration of your project.

Please feel free to do with this issue what you see fit. Many thanks.

UPDATE: I had some time this weekend to experiment with Kanata (a KMonad clone) and after quite a bit of fiddling I achieved what I described:

(defalias 
  pow (tap-hold 200 200 (tap-dance 200 (caps (layer-toggle numpad))) (layer-toggle extend))
  ...
)

Please don't draw any inspiration from this. ๐Ÿ™