aboutsummaryrefslogtreecommitdiff
path: root/qmk/keyboards/sofle_choc/keymaps/custom/quad_tapdance.c
blob: f0a5dbb7f9ec537e7f38cf6d6a93f0ec405a14ce (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
/* 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),
};