SourceChord

C#とXAML好きなプログラマの備忘録。最近はWPF系の話題が中心です。

Electronでアプリのメニューを作る

今度は、Electron+TypeScriptなアプリにメニューを追加してみます。

Electronのアプリで使えるメニューには、ウィンドウ上部(Macの場合ディスプレイの上)に表示されるアプリケーションメニューと、画面右クリック時に表示されるコンテキストメニューの2種類のものがあります。

Electronを使ってそれぞれ順番に作っていってみます。

参考

公式ドキュメントの↓あたりを参考にいろいろやってみます。
http://electron.atom.io/docs/v0.34.0/api/menu/
http://electron.atom.io/docs/v0.34.0/api/menu-item/

以下では、前回の↓サンプルコードをベースに色々やってみました。
TypeScript+Electronでデスクトップアプリを作ってみる - SourceChord

アプリケーションメニューの作成

メニューをつけるには、Menuモジュールを利用します。
このモジュールはBrowserProcess用のモジュールなので、RendererProcessから利用する場合には、remote.require('Menu')とする必要があります。

アプリ全体のメニューを設定する

アプリのエントリポイントとなるmain.tsでメニューの設定をしてみます。

まずは、Menu.buildFromTemplateメソッドで、メニューの項目などを作成します。
そして、作ったメニューをMenu.setApplicationMenuメソッドの引数に渡すと、メニューをつけることができます。

main.ts

import app = require('app');
import BrowserWindow = require('browser-window');
import Menu = require('menu');
require('crash-reporter').start();

// メインウィンドウの参照をグローバルに持っておく。
var mainWindow: GitHubElectron.BrowserWindow = null;

var menu = Menu.buildFromTemplate([
  {
    label: 'File',
    submenu: [
      {label: 'New File'},
      {type: 'separator'},
      {label: 'Save'},
      {label: 'Save As...'},
      {type: 'separator'},
      {label: 'Exit', click: onExit}
    ]
  },
  {
    label: 'Edit',
    submenu: [
      {label: 'Copy', accelerator: 'CmdOrCtrl+C'},
      {label: 'Paste', accelerator: 'CmdOrCtrl+V'},
    ]
  },
  {
    label: 'Help',
    submenu: [
      {label: 'About'}
    ]
  },
]);

Menu.setApplicationMenu(menu);

// すべてのウィンドウが閉じられた際の動作
app.on('window-all-closed', function() {
  // OS X では、ウィンドウを閉じても一般的にアプリ終了はしないので除外。
  if (process.platform != 'darwin') {
    app.quit();
  }
});

app.on('ready', function() {
  // 新規ウィンドウ作成
  mainWindow = new BrowserWindow({ width: 800, height: 600 });

  // index.htmlを開く
  mainWindow.loadUrl('file://' + __dirname + '/index.html');

  // ウィンドウが閉じられたら、ウィンドウへの参照を破棄する。
  mainWindow.on('closed', function() {
    mainWindow = null;
  });
});


function onExit(){
  app.quit();
}

f:id:minami_SC:20151027010940p:plain

こんな風にちゃんとメニューが表示されました。

このメソッドを使ってメニューをつけた場合、BrowserWindow側で別途メニューの設定をしていない限り、すべてのウィンドウについて、ここでセットしたメニューが表示されるようです。

指定したウィンドウにメニューをつける

BrowserWindowクラスのsetMenuというメソッドでもメニューをつけることができます。
こうすると、setMenuメソッドを呼び出したウィンドウについてだけメニューをつけられるみたい。

  mainWindow = new BrowserWindow({ width: 800, height: 600 });
  mainWindow.setMenu(menu);

コンテキストメニューの作成

続いてコンテキストメニューを作ってみます。

ウィンドウ上を右クリックしたときの'contextmenu'イベントのタイミングでポップアップ表示をします。 UI上のイベントを拾うので、今度はRendererProcessからメニュー表示をしてみます。

コンテキストメニューも、前述のアプリケーションメニューと同じように、まずはMenu.buildFromTemplateメソッドでMenuクラスのインスタンスを作ります。

コンテキストメニューとして表示するには、Menu.popupメソッドを呼び出します。
引数には、ポップアップを表示させるウィンドウを指定します。

index.ts

var remote = require('remote');
var app = remote.require('app');
var BrowserWindow = remote.require('browser-window');
var Menu = remote.require('menu');

var menu = Menu.buildFromTemplate([
  {label: 'すべて選択'},
  {type: 'separator'},
  {label: 'Copy', accelerator: 'CmdOrCtrl+C'},
  {label: 'Paste', accelerator: 'CmdOrCtrl+V'}
]);

window.addEventListener('contextmenu', function (e) {
  e.preventDefault();
  menu.popup(remote.getCurrentWindow());
}, false);

f:id:minami_SC:20151027010949p:plain

その他

メニューを自動で隠す設定

アプリケーションメニューでは、BrowserWindowクラスのsetAutoHideMenuBarというメソッドを使うと、メニューを自動で隠すようにすることができます。
Altを押すとメニューが出てきます。そしてフォーカスが外れると自動で隠れます。

mainWindow.setAutoHideMenuBar(true);

そんなに頻繁には使わないかもしれないけど、ウィンドウの領域を極力広く使いたいようなケースでは使うこともあるのかな。