OS X用Electronアプリで，closeボタン処理と終了処理を区別する

# 注意

この記事はElectron v0.31.0 仕様です． 2016/5/25現在，Electronのバージョンが1.1.3になり以下のコードは動きません．
[OS X用Electronアプリで，closeボタン処理と終了処理を区別する（Electron v1.1）](http://qiita.com/yukiB/items/787a567777c0542f0e74)を参考にしてください．

# はじめに

[Electron](http://electron.atom.io/)とは，Atomエディタを開発するために生まれたCross Platform Desktop Application Engineです.
Node.js + HTML + CSSでアプリケーションを作成でき，同じコードでWindows，Linux，OS X用のアプリケーションを生成することができます．Electronを使ったアプリケーションの作り方は，すでに様々なサイトで紹介されていますのでそちらを参考にしてください．
本エントリーでは，Electronアプリの終了処理について紹介し，OS Xアプリ用の終了処理の書き方について説明します．

## 環境

- mac osx 10.10.3
- Electron v0.31.0


# Electronアプリの終了処理

Electronアプリが終了しようとする際は，BrowserWindowの`close`イベントが呼び出されます．アプリウィンドウの終了ボタン，MenuやDockアイコンからの終了，ショートカットキーによる終了，OSのシャットダウンに伴い呼び出される終了処理時に本イベントが呼び出されます．
また，アプリ終了時に全てのウィンドウが閉じた際に`window-all-closed`イベントが呼び出されます．

これらの挙動を確かめるには以下のコードを実行してみてください(Electronは既にインストール済みであるとします)．
プロジェクトディレクトリを作成し．．．

```
mkdir electron-close-test; cd electron-close-test
```
```
npm init -y
```

package.jsonを書き換え．．．

```json:package.json
{
  ...
  "main": "main.js",
  ...
}
```

main.jsを作成します．

```js:main.js
var app = require('app');
var BrowserWindow = require('browser-window');
// You should create mainWindow as global variable to prevent GC destroying it automatically
var mainWindow = null;

 // Prevent shutdown until Cmd+Q are typed in Mac(darwin) OS
app.on('window-all-closed', function(){
    console.log("window-all-closed");
    if(process.platform != 'darwin')
        app.quit();
});

app.on('ready', function(){
    mainWindow = new BrowserWindow({width:800, height:600});

    mainWindow.on('close', function(e){
        console.log("close");
    });
});

```

## OS X用アプリの終了処理

上記のプログラムではOSX環境の場合，アプリウィンドウのcloseボタンを押した際に，アプリは終了しませんがウィンドウ自体は閉じてしまいます．OSXアプリでは，closeボタンを押した際にはウィンドウが隠れ，ドックのアイコンをクリックするとウィンドウが再度表示されるような挙動が望ましいため，そのような処理を記述します．

その際，appの`before-quit`イベントと，`activate-with-no-open-windows`イベントを使用します．
`before-quit`イベントは，メニューアイテムやキーボードショートカットによる終了，OSシャットダウンによる強制終了時に呼び出され，closeボタンを押した際には呼び出されません．
また，`activate-with-no-open-windows`イベントはDockのアイコンをクリックした際に呼び出されます．
それらを使い，OSX用アプリの挙動を記述します．

```js:main.js
var app = require('app');
var BrowserWindow = require('browser-window');
var Menu = require('menu');

var force_quit = false;
var menu = Menu.buildFromTemplate([
{
    label: 'Sample',
    submenu: [
        {label: 'About App', selector: 'orderFrontStandardAboutPanel:'},
        {
            label: 'Quit', 
            accelerator: 'CmdOrCtrl+Q', 
            click: function() { 
                //force_quit=true; 
                app.quit();
            }
        }
    ]
}]);

app.on('window-all-closed', function(){
    console.log("window-all-closed");
    if(process.platform != 'darwin')
        app.quit();
});

// This is another place to handle events after all windows are closed
app.on('will-quit', function () {
    // This is a good place to add tests insuring the app is still
    // responsive and all windows are closed.
    console.log("will-quit");
    mainWindow = null;
});

app.on('ready', function(){

    Menu.setApplicationMenu(menu);

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

    // Continue to handle mainWindow "close" event here
    mainWindow.on('close', function(e){
        console.log("close");
        if(!force_quit){
            e.preventDefault();
            mainWindow.hide();
        }
    });

    // You can use 'before-quit' instead of (or with) the close event
    app.on('before-quit', function (e) {
        // Handle menu-item or keyboard shortcut quit here
        console.log("before-quit");
        force_quit = true;
    });

    app.on('activate-with-no-open-windows', function(){
        mainWindow.show();
    });
});

```
`before-quit`イベントにより，closeボタンが押された処理と，終了処理とを区別し，
closeボタンが押された場合は，`e.preventDefault();`によって終了処理を中断し，`mainWindow.hide();`によってウィンドウを非表示にしています．

以上で，closeボタンを押した場合はウィンドウが非表示になり，アイコンがクリックされた際は再び表示されるといった挙動を実装することができます．