TapDance on Ctrl Key, can I MoveToLayer...?

Hi!
I’m really struggling to get my head around how the TapDance plugin works; I wanted some help to check that I’m even using the right plugin to do what I am trying to do (I’ve also spent some time reading MagicKeys, QuKeys and Leader plugins - and think there might be some overlap).

How I would want this to work:

  • When I press this key once and release it quickly without pressing any other key, it sends Ctrl
  • When I press and hold this key and press {another key} at the same time within a short window, it sends Ctrl+{another key}
  • When I press and release this key twice quickly in succession, it moves to another layer
  • If I press this key more than twice, I don’t really care what happens… I can work with anything :slight_smile:

Does this sound like something which TapDance could do, or should I look at a different plugin?

Even better… does anyone have any example code that might help me figure out how this should work?! I can’t work out how TapDance works if I’m not returning a keypress, but want instead to send a modifier or execute a function.

Out of curiosity, what purpose does this serve? Most of the time, modifiers pressed and released on their own have no effect; do you want it to be a OneShot control?

For the rest of it, we should start with the fact that you want the key’s behaviour to distinguish between a tap and a hold, for which you should use Qukeys. Next, you want its output when tapped to depend on the number of taps, for which you need TapDance. Fortunately, these two plugins can be combined and used on the same key, if you’re careful.

In your keymap, you can define a key as TD(0); that sets up a TapDance key with index 0. Then you’ll need to add the following code to your sketch:

void tapDanceAction(uint8_t tap_dance_index, KeyAddr key_addr, uint8_t tap_count, kaleidoscope::plugin::TapDance::ActionType tap_dance_action) {
  switch (tap_dance_index) {
  case 0:
    return tapDanceActionKeys(tap_count, tap_dance_action, Key_LeftControl, LockLayer(1));
  }
}

This will set up that key such that one tap will result in control, and two taps will activate Layer 1. You could change that to MoveToLayer(1), if you prefer to also turn off other active layers.

In addition, you’ll need to define a Qukey object. Inside your sketch’s setup() function, add this (replacing the key coordinates with the layer, row, and column numbers corresponding to the TD(0) key in your keymap):

void setup() {
  QUKEYS(
    kaleidoscope::plugin::Qukey(0, KeyAddr(2, 3), Key_LeftControl)
  );
  // The rest of setup() goes here...
}

This defines a single Qukey on layer 0, row 2, column 3, which will become Key_LeftControl if used in combination with another key or held long enough on its own.

Note: The order of the plugins is important. You should have the following elsewhere in your sketch:

KALEIDOSCOPE_INIT_PLUGINS(
  Qukeys,
  TapDance,
  // other plugins go after those two...
);

It’s important to discriminate between a “tap” and a “hold” first, then to count taps to determine what to do. And for now, it’s especially important to put them in this order, because TapDance has a bug where you’ll get a tremendous number of on-off cycles if you hold a TapDance key past its timeout value. Having Qukeys first will prevent TapDance from getting that trigger.

2 Likes

Re: “keypress” vs “modifier” — Are you thinking of using this key as a OneShot modifier? That would mean a key you can press & release, and the modifier stays active and applies to the following keypress.

Re: “execute a function” — This is fairly easy:

void tapDanceAction(uint8_t tap_dance_index, KeyAddr key_addr, uint8_t tap_count, kaleidoscope::plugin::TapDance::ActionType tap_dance_action) {
  switch (tap_dance_index) {
  case 0:
    if (tap_dance_action == kaleidoscope::plugin::TapDance::Interrupt ||
        tap_dance_action == kaleidoscope::plugin::TapDance::Timeout) {
      if (tap_count == 1) {
        // Do something here...
      }
    }
    return tapDanceActionKeys(tap_count, tap_dance_action, Key_LeftControl, LockLayer(1));
  }
}

See the TapDance documentation’s “How does it work?” section for more details.

2 Likes

Thanks so much for your help @merlin ! This made perfect sense, and did exactly what I was hoping for.

To answer your question:

…it’s a fairly strange behaviour, I know… but there’s a feature in Windows where a single press of the Ctrl key will highlight the location of your cursor on the screen - I have a fairly wide monitor, and I often get lost! I use this to work out where the cursor is.

One odd quirk that I’ve found since I implemented this is that somewhere in the implementation of the tapDance action it’s letting through an additional keystroke before it takes the MoveToLayer action (I chose MoveToLayer rather than LockToLayer, it works better for my needs).

For example, if I hit and release Ctrl twice (as described above) and then press another key after a short gap, the next key to be struck reports the action as though the layer hasn’t yet been changed, but the one after that works as expected, without having to press the Ctrl key again. I wondered if this is some quirk about the way timeouts work between the two plugins or something… at any rate, I’m able to work around it in most scenarios - just thought it was worth mentioning in case you had any ideas!

2 Likes

I think I know what’s going on (but I’m just speculating, really). It may depend on key scan order, or it might not. Either way, it should be considered a bug in TapDance. If you’re able to report it as a GitHub issue for Kaleidoscope, please do so. If not, let me know and I’ll do it when I get a chance.

1 Like

No problem, I’ve created an issue for this here - hope that it makes sense!

Thanks again for your help.

1 Like