Tap, double-tap, hold function wanted - choice of primary actions not working as expected

How do you suggest to resolve the ambiguity between ifDoubletap that looks into the past and ifDoubletap that looks into the future?

More thoughts:

ifFollowedByTapSequence => ifSequence
ifExtendedToChord => ifChord

Linguistically, FollowedBy refers to future keys. (The opposite would be PrecededBy…)

ExtendedTo also means that this key is already active and the next keys listed would be the (future) extension.

Or:

ifFollowedByTapSequence => ifSequence
ifFollowedWithChord => ifChord

ifDoubleTap into the future would be an ifFollowedBy $thiskey?

The other one would be ifPrecededBy $thiskey.

Still not intuitive way to write a ifDoubletap. Exactly the same problem as with ifGesture. You have to start thinking about workarounds before you have even a chance to decode it.

When would you want to match a future doubletap?
When would you want to match a past doubletap?

What are the use cases?

1 Like

When would you want to match a past doubletap?

Have you not read the initial posts, or are they so unintelligible? :-/

holdLayer fn
ifDoubletap toggleLayer fn
holdKey iLS //act as a normal left shift
ifDoubletap tapKey capsLock //lock "shift" by doubletapping shift

When would you want to match a future doubletap?

I don’t have a real-life example of mine, but clearly rpnfan has one in the first post.

For an artificial one, assume I want a key to produce “b” on tap and “a” on doubletap.

ifGesture $thisKeyId final tapKey a
holdKey b

Or from constraints standpoint:

When would you want to match a future doubletap?

When I don’t want the side effect of the first tap, but don’t mind waiting.

When would you want to match a past doubletap?

If I don’t mind the side effects of the first tap, but want it to work in real time.

Looks like the current version of ifDoubletap matches the usual use case nicely.

If you want the more sophisticated “ignore my first press for a bit and wait for more” then you use ifSequence.

In the documentation for ifSequence and ifChord you would describe that they will delay the press a bit until more info or timeout is seen – just like ifSecondary/ifPrimary (advanced resolution strategy) may delay a bit.

In the documentation of ifDoubletap you would describe that they look back at previous activations but won’t suppress or delay those.

You could make the doubletap case clearer by naming it ifPrecededBy.

So you would end up with:

// looking at past keypresses:
ifPrecededBy $thisKeyId = ifDoubletap
ifPrecededBy ...

// looking at future keypresses:
ifFollowedBy $thisKeyId = ifSequence $thisKeyId
ifTogetherWith ... = ifChord ...

That makes a nice table (or maybe it doesn’t? but I’ll try…).

which key looking at past key taps looking at simultaneous key presses looking at future key taps looking at timeout
(separate taps: each key pressed and released individually) (chord: all keys pressed and held down simultaneously) (separate taps: each key pressed and released individually) (key held down for longer time)
other keys
(long version)
ifPrecededBy keyid ifTogetherWith keyids… ifFollowedBy keyids…
other keys
(short version)
ifAfter keyid ifChord keyids… ifSequence keyids…
other keys
(legacy name)
n/a ifShortcut keyids… ifGesture keyids…
same key ifDoubletap
or
ifAfter $thisKeyId
n/a n/a ifHeld

The commands in the columns “simultaneous” and “future” keypresses will delay the macro and wait for more keys (up to a timeout).

  • if the condition is reached, the corresponding macro action is executed, and the keys pressed so far are swallowed.
  • if the timeout is reached, the individual keys pressed so far are activated (played back).

Does this make any sense?
Will it be clearer in this tabular form?

Burn my ideas if you don’t like them. I am wearing asbestos.

1 Like

Burn my ideas if you don’t like them. I am wearing asbestos.

Well, lets say that I am not convinced.

I have to take more time to read and grasp all posts fully – as I am not confident with the different command yet.

But a quick feedback on the idea of the table. I think that is a great way to make it clearer what each command is affecting. Another idea is to draw a timeline and visualize some examples.

Throwing in some more words to see what may stick (so I don’t forget my brainstorm…):

condition meaning direction
ifTappedPreviously doubletap past
ifTappedAgain doubletap future
ifTappedBefore doubletap past
ifTappedNext ... tap sequence future
ifAlsoPressed ... chord future

Can’t stop thinking about this. There has to be a way to make this clear and easy to understand.

New idea:

What if ifDoubletap just got a qualifier, like the advancedStrategy or simpleStrategy qualifiers for ifPrimary and ifSecondary.

ifDoubletap pastStrategy ...
ifDoubletap futureStrategy ...

If the strategy field is omitted, it defaults to pastStrategy, making the behaviour backwards compatible.

If you really wanted, you could make the defaultDoubletapStrategy configurable (e.g. from $onInit), just like the defaultStrategy for secondaryRole:

set doubletap.defaultStrategy past
set doubletap.timeout 300

etc.

I think I’ve reached a conclusion for me. This is as far as I can bring this for now.

@rpnfan What do you think about this table?

category examining past keys examining simultaneous keys examining future keys timeout
taps, chords, or hold separate taps: each key pressed and released individually chord: all keys pressed and held down simultaneously separate taps: each key pressed and released individually key held down for longer time
execution strategy executes immediately delays macro execution to wait for more keys (up to timeout) delays macro execution to wait for more keys (up to timeout) delays macro execution until timeout or key release
buffered keys (held back while waiting) nothing buffered; previous keys already executed their behaviour
  • when condition is met: macro action is executed, and buffered keys are swallowed

  • when condition is not met: buffered keys are replayed
  • when condition is met: macro action is executed, and buffered keys are swallowed

  • when condition is not met: buffered keys are replayed
  • buffered keys are replayed
    commands examining other keys ifPrecededBy keyid
    or
    ifAfter keyid
    ifTogetherWith keyids…
    or
    ifChord keyids…
    ifFollowedBy keyids…
    or
    ifNext keyids…
    or
    ifSequence keyids…
    (macro execution continues; else clause taken)
    commands examining same key ifDoubletap pastStrategy
    or
    ifAfter $thisKeyId
    n/a ifDoubletap futureStrategy
    or
    ifNext $thisKeyId
    ifHeld
    or
    ifHold
    legacy commands ifDoubletap
    =
    ifDoubletap pastStrategy
    ifShortcut keyids…
    =
    ifChord keyids
    ifGesture keyids…
    =
    ifNext keyids…

    Where there are “or” choices, my personal preferences for the namings are:

    command why
    ifAfter ... “After” clearly refers to something in the past
    ifChord ... “Chord” is something played or pressed together
    ifNext ... “Next” clearly refers to the future
    ifHeld “Held” is proper English, because a key is held down
    ifDoubletap pastStrategy
    ifDoubletap futureStrategy
    Similar qualifier as for ifPrimary/ifSecondary,
    defaults to pastStrategy for backwards compatibility

    As an alternative for the doubletap strategies (= easier implementation), one could also just document that ifDoubletap is identical to ifAfter $thisKeyId, and if you wanted the “future” behaviour you would need to write ifNext $thisKeyId.

    I’m taking off the asbestos suit. Burn me.

    1 Like