従来のキーバインド設定方法
Karabiner-Elementsを使うと、MacでJISキーボードを使うときにWindowsのようなIME切り替えができるようになったり、Vimのようなカーソル移動を行ったり、あるいは色々なアプリの操作を自分流にアレンジしたりできるのですが、Emacsのctrl-x ctrl-f
のような2つのキーを連続してタイプした場合のキー変換の設定は一筋縄ではいきませんでした。
正確に言えば、ctrl-x
をタイプした後に未設定のキーを誤ってタイプした場合のエラー処理を省略すれば簡単に書けるのですが、エラー処理を行うとなると、Karabiner-Elementsで設定可能なキーが200以上あるため非常に面倒な作業になります。私がひとまずの対応策として採った方法は、pqrs-org/KE-complex_modificationsで公開されているツールを使って、設定をerb
ファイルに書いてjson
ファイルに変換するという方法です。(私が書いたこちらの記事で簡単に紹介しています)
新しいキーバインド設定方法
まだBeta版(Ver.11.1.12)でしか実現できないですが、Karabiner-Elementsに「ctrl-x
をタイプした後に未設定のキーをタイプした場合の動作を指定する設定」が加わりましたので、この設定を活用することでキー変換の設定が随分と楽になります。
新しい設定項目について
Beta版(Ver.11.1.12)でto_delayed_action
, to_if_invoked
, to_if_canceled
という3つの項目が設定項目に追加されました。この設定項目を使った場合に何ができるかを以下のコードで紹介します。
{
"description": "test to_delayed_action",
"manipulators": [
{
"type": "basic",
"from": {
"key_code": "a",
"modifiers": { "mandatory": [ "control" ] }
},
"to": [
{ "set_variable": { "name": "ctrl-a", "value": 1 } }
],
"to_delayed_action": {
"to_if_invoked": [
{ "set_variable": { "name": "ctrl-a", "value": 2 } }
],
"to_if_canceled": [
{ "set_variable": { "name": "ctrl-a", "value": 0 } }
]
}
}
]
}
このコードの動作は以下のとおりです。
- ctrl-aをタイプすると
ctrl-a
変数に1を代入する(to
のset_variable
が実行される) - そのまま何もタイプしないでいると、
ctrl-a
変数に2を代入する(to_if_invoked
のset_variable
が実行される) - ctrl-aに続けて別のキーをタイプすると、
ctrl-a
変数に0を代入する(to_if_canceled
のset_variable
が実行される)
実際のコード
これらの設定項目を利用してEmacsのような2ストロークのキーバインドを実現するコードは以下のとおりです。
{
"description": "Two stroke key_bind",
"manipulators": [
{
"type": "basic",
"from": {
"key_code": "c", "modifiers": { "mandatory": [ "control" ] }
},
"to": [
{ "key_code": "q", "modifiers": [ "command" ] }
],
"conditions": [
{ "type": "variable_if", "name": "ctrl-x", "value": 1 }
]
},
{
"type": "basic",
"from": {
"key_code": "f", "modifiers": { "mandatory": [ "control" ] }
},
"to": [
{ "key_code": "o", "modifiers": [ "command" ] }
],
"conditions": [
{ "type": "variable_if", "name": "ctrl-x", "value": 1 }
]
},
{
"type": "basic",
"from": {
"key_code": "x",
"modifiers":
{ "mandatory": [ "control" ], "optional": [ "caps_lock" ] }
},
"to": [
{ "set_variable": { "name": "ctrl-x", "value": 1 } }
],
"to_if_alone": [
{ "key_code": "x" }
],
"to_delayed_action": {
"to_if_invoked": [
{ "set_variable": { "name": "ctrl-x", "value": 0 } }
],
"to_if_canceled": [
{ "set_variable": { "name": "ctrl-x", "value": 0 } }
]
},
"conditions": [
{ "type": "variable_if", "name": "ctrl-x", "value": 0 }
]
}
]
}
このコードの動作は以下のとおりです。
- ctrl-xをタイプすると
ctrl-x
変数に1を代入する(to
のset_variable
が実行される) - 次にタイプしたキーにより、以下の処理が行われる。
- ctrl-c -> command-qに変換
- ctrl-f -> command-fに変換
- 上記1〜2以外 ->
ctrl-x
変数に0を代入する。(to_if_canceled
のset_variable
が実行される)
- ctrl-xをタイプした後、一定の時間が経過すると
ctrl-x
変数に0を代入する。(to_if_invoked
のset_variable
が実行される)
補足
上記のJSONファイルを、冒頭に挙げたpqrs-org/KE-complex_modificationsで公開されているツールを使ってerb
ファイルで書いた場合のコードも掲載します。
KE-complex_modificationsのツールの使い方は、こちらの記事で解説しています。
{
"title": "Two stroke key_bind",
"rules": [
{
"description": "Two stroke key_bind",
"manipulators": [
<%=
# control-xの次にタイプするキーを指定する
used_keys = [
["c", []],
["c", ["control"]],
["d", ["control"]],
["f", ["control"]]
]
# 変換後の組み合わせなどを指定する
to_keys = [
set_shell_command(["open -a 'Google Chrome.app'"]),
to([["q", ["command"]]]),
to([["vk_mission_control", ["command"]]]),
to([["o", ["command"]]])
]
types = ""
used_keys.each_with_index do |use_key, index|
types += "{
\"type\": \"basic\",
\"from\": #{from(use_key[0], use_key[1])},
\"to\": #{to_keys[index]},
\"conditions\": [
#{variable_if(["press_control_x_key"], [1])}
]
},"
end
types
%>
<%# set_variable, variable_if, set_shell_commandメソッドは自作 %>
{
"type": "basic",
"from": <%= from("x", ["control"], ["caps_lock"]) %>,
"to": [
<%= set_variable(["press_control_x_key"], [1]) %>
],
"to_if_alone": [
<%= to([["x"]]) %>
],
"to_delayed_action": {
"to_if_invoked": [
<%= set_variable(["press_control_x_key"], [0]) %>
],
"to_if_canceled": [
<%= set_variable(["press_control_x_key"], [0]) %>
]
},
"conditions": [
<%= variable_if(["press_control_x_key"], [0]) %>,
]
}
]
}
]
}