/* Return an integer that corresponds to what kind of tap dance should be executed. * * How to figure out tap dance state: interrupted and pressed. * * Interrupted: If the state of a dance is "interrupted", that means that another key has been hit * under the tapping term. This is typically indicitive that you are trying to "tap" the key. * * Pressed: Whether or not the key is still being pressed. If this value is true, that means the tapping term * has ended, but the key is still being pressed down. This generally means the key is being "held". * * One thing that is currenlty not possible with qmk software in regards to tap dance is to mimic the "permissive hold" * feature. In general, advanced tap dances do not work well if they are used with commonly typed letters. * For example "A". Tap dances are best used on non-letter keys that are not hit while typing letters. * * Good places to put an advanced tap dance: * z,q,x,j,k,v,b, any function key, home/end, comma, semi-colon * * Criteria for "good placement" of a tap dance key: * Not a key that is hit frequently in a sentence * Not a key that is used frequently to double tap, for example 'tab' is often double tapped in a terminal, or * in a web form. So 'tab' would be a poor choice for a tap dance. * Letters used in common words as a double. For example 'p' in 'pepper'. If a tap dance function existed on the * letter 'p', the word 'pepper' would be quite frustating to type. * * For the third point, there does exist the 'TD_DOUBLE_SINGLE_TAP', however this is not fully tested * */ #include QMK_KEYBOARD_H #include "keymap_bepo.h" #include "quad_tapdance.h" #include "keycodes.h" td_state_t cur_dance(tap_dance_state_t *state) { if (state->count == 1) { // The function can be called when the delay is over, or because of // another tap. // I do not check here if the delay is over, as soon as another tap // occurs, I check the state of the key and switch in hold mode if the // key is still pressed. // if (!state->pressed) return TD_SINGLE_TAP; // Key has not been interrupted, but the key is still held. Means you want to send a 'HOLD'. else return TD_SINGLE_HOLD; } else if (state->count == 2) { // TD_DOUBLE_SINGLE_TAP is to distinguish between typing "pepper", and actually wanting a double tap // action when hitting 'pp'. Suggested use case for this return value is when you want to send two // keystrokes of the key, and not the 'double tap' action/macro. if (state->interrupted) return TD_DOUBLE_SINGLE_TAP; else if (state->pressed) return TD_DOUBLE_HOLD; else return TD_DOUBLE_TAP; } // Assumes no one is trying to type the same letter three times (at least not quickly). // If your tap dance key is 'KC_W', and you want to type "www." quickly - then you will need to add // an exception here to return a 'TD_TRIPLE_SINGLE_TAP', and define that enum just like 'TD_DOUBLE_SINGLE_TAP' if (state->count == 3) { if (state->interrupted || !state->pressed) return TD_TRIPLE_TAP; else return TD_TRIPLE_HOLD; } else return TD_UNKNOWN; } // // Definition for the key W. // // The main usage is to send the letter W when pressed, but the key is also // used to activate the SHIFT mode when pressed. // // If the key is double tapped, it will also switch into the caps_word mode. // static td_tap_t w_tap_state = { .is_press_action = true, .state = TD_NONE }; void w_finished(tap_dance_state_t *state, void *user_data) { w_tap_state.state = cur_dance(state); switch (w_tap_state.state) { case TD_SINGLE_TAP: register_code(BP_W); break; case TD_SINGLE_HOLD: register_code(KC_RIGHT_SHIFT); break; case TD_DOUBLE_TAP: caps_word_on(); break; case TD_DOUBLE_SINGLE_TAP: tap_code(BP_W); register_code(BP_W); break; default: break; } } void w_reset(tap_dance_state_t *state, void *user_data) { switch (w_tap_state.state) { case TD_SINGLE_TAP: unregister_code(BP_W); break; case TD_SINGLE_HOLD: unregister_code(KC_RIGHT_SHIFT); break; case TD_DOUBLE_SINGLE_TAP: unregister_code(BP_W); break; default: break; } w_tap_state.state = TD_NONE; } // // Definition for the layer key // // The only one usage is to activate the layer over the keyboard, but there is // two way of doing it: // // The first mode, when hold, is to activate the layer as long as the key is pressed. // The second one is to switch in the layer mode when double tapped, in this // mode, you can go back into the normal layer by a single press on the key. // static td_tap_t ql_tap_state = { .is_press_action = true, .state = TD_NONE }; // Functions that control what our tap dance key does void ql_finished(tap_dance_state_t *state, void *user_data) { ql_tap_state.state = cur_dance(state); switch (ql_tap_state.state) { // Remove the layer with a single tap, this way I always have a key to remove the // the layer, without knowing the previous state I had. case TD_SINGLE_TAP: layer_off(LAYER_SYMBOLS); break; case TD_SINGLE_HOLD: layer_on(LAYER_SYMBOLS); break; case TD_DOUBLE_TAP: layer_invert(LAYER_SYMBOLS); break; default: break; } } void ql_reset(tap_dance_state_t *state, void *user_data) { // If the key was held down and now is released then switch off the layer if (ql_tap_state.state == TD_SINGLE_HOLD) { layer_off(LAYER_SYMBOLS); } ql_tap_state.state = TD_NONE; } // // Definiton for the key L_SHIFT. // The main usage of the key is to activate the LShift when hold. But the key // can also be used to active the CAPSLOCK mode. // // static td_tap_t lshift_tap_state = { .is_press_action = true, .state = TD_NONE }; // Functions that control what our tap dance key does void lshift_finished(tap_dance_state_t *state, void *user_data) { lshift_tap_state.state = cur_dance(state); switch (lshift_tap_state.state) { // Remove the layer with a single tap, this way I always have a key to remove the // the layer, without knowing the previous state I had. case TD_SINGLE_HOLD: register_code(KC_LEFT_SHIFT); break; case TD_SINGLE_TAP: if (host_keyboard_led_state().caps_lock) register_code(KC_CAPS_LOCK); break; default: break; } } void lshift_reset(tap_dance_state_t *state, void *user_data) { switch (lshift_tap_state.state) { case TD_SINGLE_HOLD: unregister_code(KC_LEFT_SHIFT); break; case TD_SINGLE_TAP: if (host_keyboard_led_state().caps_lock) unregister_code(KC_CAPS_LOCK); break; default: break; } lshift_tap_state.state = TD_NONE; } // // Declare the tapdance table here. // tap_dance_action_t tap_dance_actions[] = { [TD_C_CCED] = ACTION_TAP_DANCE_DOUBLE(BP_C, BP_CCED), [TD_PRC] = ACTION_TAP_DANCE_DOUBLE(BP_EQL, BP_PERC), [TD_W_CAPSLOCK] = ACTION_TAP_DANCE_FN_ADVANCED(NULL, w_finished, w_reset), [TD_LAYER_SYMB] = ACTION_TAP_DANCE_FN_ADVANCED(NULL, ql_finished, ql_reset), [TD_LSFT] = ACTION_TAP_DANCE_FN_ADVANCED(NULL, lshift_finished, lshift_reset), [TD_EE] = ACTION_TAP_DANCE_DOUBLE(BP_COMM, BP_EGRV), };