Help us understand the problem. What is going on with this article?

Karabiner-Elementsの設定項目が増えてEmacsライクな設定が楽になった

More than 1 year has passed since last update.

従来のキーバインド設定方法

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つの項目が設定項目に追加されました。この設定項目を使った場合に何ができるかを以下のコードで紹介します。

test.json
{
  "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 } }
        ]
      }
    }
  ]
}

このコードの動作は以下のとおりです。

  1. ctrl-aをタイプするとctrl-a変数に1を代入する(toset_variableが実行される)
  2. そのまま何もタイプしないでいると、ctrl-a変数に2を代入する(to_if_invokedset_variableが実行される)
  3. ctrl-aに続けて別のキーをタイプすると、ctrl-a変数に0を代入する(to_if_canceledset_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 }
      ]
    }
  ]
}

このコードの動作は以下のとおりです。

  1. ctrl-xをタイプするとctrl-x変数に1を代入する(toset_variableが実行される)
  2. 次にタイプしたキーにより、以下の処理が行われる。
    1. ctrl-c -> command-qに変換
    2. ctrl-f -> command-fに変換
    3. 上記1〜2以外 -> ctrl-x変数に0を代入する。(to_if_canceledset_variableが実行される)
  3. ctrl-xをタイプした後、一定の時間が経過するとctrl-x変数に0を代入する。(to_if_invokedset_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]) %>,
          ]
        }
      ]
    }
  ]
}
s-show
Why not register and get more from Qiita?
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away
Comments
No comments
Sign up for free and join this conversation.
If you already have a Qiita account
Why do not you register as a user and use Qiita more conveniently?
You need to log in to use this function. Qiita can be used more conveniently after logging in.
You seem to be reading articles frequently this month. Qiita can be used more conveniently after logging in.
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away
ユーザーは見つかりませんでした