Thunderbirdをカーソルキーでメールを読む

Thunderbird(Version 115系)のアドオン「tbkeys」を使ってカーソルキーのみでメールを選択して本文を読めるようにする設定を紹介する。

カーソルキーの操作で行いたいこと

フォーカスの位置毎にどのような振る舞いを行いたいかを表にした。

フォーカス箇所 rightキー leftキー upキー downキー
Folder Threadにフォーカスを移動 メッセージブラウザーを隠す (上位フォルダーへ移動) メッセージブラウザーを隠す (上のフォルダーへ移動) (下のフォルダーへ移動)
Thread Messageにフォーカスを移動 メッセージブラウザーを表示 Folderにフォーカスを移動 メッセージブラウザーを隠す (上のスレッドに移動) (下のスレッドに移動)
Message メッセージブラウザーを表示 Threadにフォーカスを移動 メッセージブラウザーを隠す (メッセージを上にスクロール) (メッセージを下にスクロール)
それ以外 Threadにフォーカスを移動 メッセージブラウザーを隠す Threadにフォーカスを移動 メッセージブラウザーを隠す - -

対応するJavascript

leftキー

let tabmail = window.document.getElementById('tabmail');
let focusedElement = window.document.activeElement;
let { currentTabInfo } = tabmail;
if (currentTabInfo.mode.name == 'mail3PaneTab') {
 let { contentWindow, contentDocument } = currentTabInfo.chromeBrowser;
 let { paneLayout, folderTree, threadTree, webBrowser, messageBrowser, multiMessageBrowser, accountCentralBrowser, } = contentWindow;
 if (focusedElement == currentTabInfo.chromeBrowser) {
  focusedElement = contentDocument.activeElement;
  if (focusedElement != folderTree && contentDocument.getElementById('folderPane').contains(focusedElement)) {
   focusedElement = folderTree;
  } else if (contentDocument.getElementById('threadPaneNotificationBox').contains(focusedElement)) {
   focusedElement = threadTree.table.body;
  } else if (focusedElement != threadTree.table.body && contentDocument.getElementById('threadPane').contains(focusedElement)) {
   focusedElement = threadTree.table.body;
  } else if (focusedElement == messageBrowser) {
   focusedElement = messageBrowser.contentWindow;
  }
  if (focusedElement == folderTree) {
   ;
  } else if (focusedElement == threadTree.table.body) {
   folderTree.focus();
  } else if (focusedElement == messageBrowser.contentWindow) {
   threadTree.table.body.focus();
  } else {
   threadTree.table.body.focus();
  }
 }
 paneLayout.messagePaneSplitter.collapse();
}

rightキー

let tabmail = window.document.getElementById('tabmail');
let focusedElement = window.document.activeElement;
let { currentTabInfo } = tabmail;
if (currentTabInfo.mode.name == 'mail3PaneTab') {
 let { contentWindow, contentDocument } = currentTabInfo.chromeBrowser;
 let { paneLayout, folderTree, threadTree, webBrowser, messageBrowser, multiMessageBrowser, accountCentralBrowser, } = contentWindow;
 if (focusedElement == currentTabInfo.chromeBrowser) {
  focusedElement = contentDocument.activeElement;
  if (focusedElement != folderTree && contentDocument.getElementById('folderPane').contains(focusedElement)) {
   focusedElement = folderTree;
  } else if (contentDocument.getElementById('threadPaneNotificationBox').contains(focusedElement)) {
   focusedElement = threadTree.table.body;
  } else if (focusedElement != threadTree.table.body && contentDocument.getElementById('threadPane').contains(focusedElement)) {
   focusedElement = threadTree.table.body;
  } else if (focusedElement == messageBrowser) {
   focusedElement = messageBrowser.contentWindow;
  }
  if (focusedElement == folderTree) {
   threadTree.table.body.focus();
   paneLayout.messagePaneSplitter.collapse();
  } else if (focusedElement == threadTree.table.body) {
   paneLayout.messagePaneSplitter.expand();
   messageBrowser.contentWindow.focus();
  } else if (focusedElement == messageBrowser.contentWindow) {
   paneLayout.messagePaneSplitter.expand();
  } else {
   threadTree.table.body.focus();
   paneLayout.messagePaneSplitter.collapse();
  }
 }
}

tbkeyの設定

上記の設定をtbkeyで行うためにはJavascriptを一行にするなどの制約があるため難読だが下記の設定をtbkeyのMain key bindingsで行う。

{
    "left": "eval(\"let tabmail = window.document.getElementById('tabmail'); let focusedElement = window.document.activeElement; let { currentTabInfo } = tabmail; if (currentTabInfo.mode.name == 'mail3PaneTab') { let { contentWindow, contentDocument } = currentTabInfo.chromeBrowser; let { paneLayout, folderTree, threadTree, webBrowser, messageBrowser, multiMessageBrowser, accountCentralBrowser, } = contentWindow; if (focusedElement == currentTabInfo.chromeBrowser) { focusedElement = contentDocument.activeElement; if (focusedElement != folderTree && contentDocument.getElementById('folderPane').contains(focusedElement)) { focusedElement = folderTree; } else if (contentDocument.getElementById('threadPaneNotificationBox').contains(focusedElement)) { focusedElement = threadTree.table.body; } else if (focusedElement != threadTree.table.body && contentDocument.getElementById('threadPane').contains(focusedElement)) { focusedElement = threadTree.table.body; } else if (focusedElement == messageBrowser) { focusedElement = messageBrowser.contentWindow; } if (focusedElement == folderTree) { ; } else if (focusedElement == threadTree.table.body) { folderTree.focus(); } else if (focusedElement == messageBrowser.contentWindow) { threadTree.table.body.focus(); } else { threadTree.table.body.focus(); } } paneLayout.messagePaneSplitter.collapse(); }\")",
    "right": "eval(\"let tabmail = window.document.getElementById('tabmail'); let focusedElement = window.document.activeElement; let { currentTabInfo } = tabmail; if (currentTabInfo.mode.name == 'mail3PaneTab') { let { contentWindow, contentDocument } = currentTabInfo.chromeBrowser; let { paneLayout, folderTree, threadTree, webBrowser, messageBrowser, multiMessageBrowser, accountCentralBrowser, } = contentWindow; if (focusedElement == currentTabInfo.chromeBrowser) { focusedElement = contentDocument.activeElement; if (focusedElement != folderTree && contentDocument.getElementById('folderPane').contains(focusedElement)) { focusedElement = folderTree; } else if (contentDocument.getElementById('threadPaneNotificationBox').contains(focusedElement)) { focusedElement = threadTree.table.body; } else if (focusedElement != threadTree.table.body && contentDocument.getElementById('threadPane').contains(focusedElement)) { focusedElement = threadTree.table.body; } else if (focusedElement == messageBrowser) { focusedElement = messageBrowser.contentWindow; } if (focusedElement == folderTree) { threadTree.table.body.focus(); paneLayout.messagePaneSplitter.collapse(); } else if (focusedElement == threadTree.table.body) { paneLayout.messagePaneSplitter.expand(); messageBrowser.contentWindow.focus(); } else if (focusedElement == messageBrowser.contentWindow) { paneLayout.messagePaneSplitter.expand(); } else { threadTree.table.body.focus(); paneLayout.messagePaneSplitter.collapse(); } } }\")"

}

この設定にたどり着くまで

F6キーでフォーカスを移動させることに似ていることからどのようなJavascriptになっているかを探すことから始まった。

  1. 「ツール」メニューから「開発ツール」→「開発ツールボックス」を選ぶ。
  2. リモートデバッグの接続を許可する。
  3. 「インスペクター」を選ぶ。
  4. keysetが怪しいと睨む
  5. keyset id="mailKeys"の中にkey keycode="VK_F6"があることに気づく。
  6. 呼び出す関数はSwitchPaneFocus()であることが分かる。
  7. 「デバッガー」に切り替え「検索」を選び、SwitchPaneFocusを検索する。
  8. function SwitchPaneFocus(event)が見つかる。
  9. 「コンソール」に切り替え、上記の関数の中身を理解するために1行ずつ実行する。