Allow double tap key to output a unique keybind without breaking hold
I'd like to do the following remapping:
- (single tap) ; to
;but only after a delay (to allow for a double tap/hold to have a different functionality), so this setstap︔variable to 1 - (single tap+X) ;x(any key≠;) to
;x(no change in behavior here), this resetstap︔back to 0 - (double tap) ;; to
:(replace semicolon with colon on double tap), this works by making ; withtap︔=1condition output ⇧; and resettap︔back to 0 - (hold) ; to enter a new mode via setting
hold︔variable to 1 (and resetting it back to 0 on release)
And it seems to me that it's currently impossible since same-key tap is treated as cancelled and same-key hold is reated as invoked, so there is no place for me to put ; output that would not break either double tap (and output :; instead of :) or hold (and output ; on hold)
I'm using the following complex-modifications features
to_if_held_downsetshold︔to 1to_after_key_upresets thehold︔back to 0, works fineto_if_alonesetsvarSemicolonTapvariable to 1 (or regulartowoks)to_delayed_actionis where the main issue is, I need it to resetvarSemicolonTapback to 0 after some delay, but also print the actual;to_if_canceledthis helps with rule 2 as any x≠; in treated as cancellation, so if I output;here it's inserted beforexjust fine. However it's also triggered when I tap ; again, and this is the buggy part. I don'tto_if_invokedworks fine with printing;after a delay but then holding the key is also treated as key being invoked, so on hold I get an extra;I don't need
Not sure what the best resolution is but maybe
Either to_if_canceled could be optionally split into 2 conditions:
to_if_canceled_by_selfto_if_canceled_by_other
Or to_if_invoked could be optionally split into 2 conditions:
to_if_invoked_unless_holdto_if_invoked_hold
Then I could skip printing ; on double tap in the cancelled clause, only reserving it for ;x
Or I could print ; only at the to_if_invoked_unless_hold call while my hold will work by just changing the variables, but not printing anything
below is a snippen of goku config (much more readable and easier to reason about vs the default)
{:des " ;×2 ⟶ : hold; ⟶ layer" :rules [
[:semicolon [:!Ssemicolon ["tap︔"0]["q︔"0]] [["tap︔"1]] ]
[:q [:semicolon :w ["tap︔"0]["q︔"1]] [["tap︔"1]] ] ;w to test correctness
; ↑ this is a poor substitute for :canceled since fails on quick ;q typing
[:semicolon nil [] {
:alone[["tap︔"1]] ; adding :semicolon here means...
;:alone[:semicolon ["tap︔"1]] ; ...double tap prints ;:
:delayed{ ;
;:invoked[{:set["tap︔"0] :halt true}] ; adding :semicolon here means...
:invoked[:semicolon {:set["tap︔"0] :halt true}] ; ... hold prints ;
:canceled[["tap︔"0]]} ; adding :semicolon here means...
;:canceled[:semicolon ["tap︔"0]]} ;... double tap prints :;
; but not adding it here means extra manual bindings bindings to manually enable the missing `;x`, and those bindings are a poor substitute as they fail on quick typing
:held[{:set["hold︔"1]} {:set["tap︔"0] :repeat false}] :afterup[["hold︔"0]]
:params{:alone 514 :held 515 :delay 515} }];
]} {
"description" : " ;×2 ⟶ : hold; ⟶ layer",
"manipulators" : [ {
"from" : {
"key_code" : "semicolon"
},
"to" : [ {
"key_code" : "semicolon",
"modifiers" : [ "left_shift" ]
}, {
"set_variable" : {
"name" : "tap︔",
"value" : 0
}
}, {
"set_variable" : {
"name" : "q︔",
"value" : 0
}
} ],
"conditions" : [ {
"name" : "tap︔",
"value" : 1,
"type" : "variable_if"
} ],
"type" : "basic"
}, {
"from" : {
"key_code" : "q"
},
"to" : [ {
"key_code" : "semicolon"
}, {
"key_code" : "w"
}, {
"set_variable" : {
"name" : "tap︔",
"value" : 0
}
}, {
"set_variable" : {
"name" : "q︔",
"value" : 1
}
} ],
"conditions" : [ {
"name" : "tap︔",
"value" : 1,
"type" : "variable_if"
} ],
"type" : "basic"
}, {
"to_if_alone" : [ {
"set_variable" : {
"name" : "tap︔",
"value" : 1
}
} ],
"to_if_held_down" : [ {
"set_variable" : {
"name" : "hold︔",
"value" : 1
}
}, {
"set_variable" : {
"name" : "tap︔",
"value" : 0
},
"repeat" : false
} ],
"to_after_key_up" : [ {
"set_variable" : {
"name" : "hold︔",
"value" : 0
}
} ],
"to_delayed_action" : {
"to_if_invoked" : [ {
"key_code" : "semicolon"
}, {
"set_variable" : {
"name" : "tap︔",
"value" : 0
},
"halt" : true
} ],
"to_if_canceled" : [ {
"set_variable" : {
"name" : "tap︔",
"value" : 0
}
} ]
},
"parameters" : {
"basic.to_if_alone_timeout_milliseconds" : 514,
"basic.to_if_held_down_threshold_milliseconds" : 515,
"basic.to_delayed_action_delay_milliseconds" : 515
},
"from" : {
"key_code" : "semicolon"
},
"conditions" : [ ],
"type" : "basic"
} ]
},Source: pqrs-org/Karabiner-Elements