Mac用ウィンドウマネージャ Slate

# Mac用ウィンドウマネージャ Slate

## はじめに
Mac用のウィンドウマネージャ(ウィンドウの整列やサイズ調整を行うソフトウェア) Slateを紹介します。

- [jigish/slate](https://github.com/jigish/slate)



## 導入
[homebrew](https://brew.sh/index_ja) の導入については割愛します。

```sh
$ brew install --cask slate
```

Slate 実行後 システム環境設定 -> プライバシー で Slate.app にチェックマークを入れる必要があります。



## 設定例
設定ファイルはホームディレクトリ直下 .slate.js です。
Javascriptで記述されるので Javascriptに慣れた方にとってはとっつきやすいかも知れません。
(ただしES5以前の仕様であることにはご注意下さい)

### 関数

```js
var util = {
  // アプリ起動・フォーカス
  // http://d.hatena.ne.jp/sugyan/20130301/1362129310
  launch_and_focus: function (target) {
    return function (win) {
      var apps = [];
      S.eachApp(function (app) { apps.push(app.name()); });
      if (! _.find(apps, function (name) { return name === target; })) {
        win.doOperation(
          S.operation('shell', {
            command: "/usr/bin/open -a " + target,
            waithFoeExit: true
          })
        );
      }
      win.doOperation(S.operation('focus', { app: target }));
    };
  },

  // ラッパー
  // http://www.infiniteloop.co.jp/blog/2013/08/osx_slate/
  //
  //   util.key('x')          -> x:alt
  //   util.key('x', 'shift') -> x:alt,shift
  key: function(k, mod) {
    return k + ':alt' + (mod ? ',' + mod : '');
  }
};
```

### アプリランチャー

```js
slate.bind(util.key('r'), util.launch_and_focus('Reeder'));
slate.bind(util.key('b'), util.launch_and_focus('Safari'));
slate.bind(util.key('t'), util.launch_and_focus('iTerm'));
slate.bind(util.key('m'), util.launch_and_focus('Music'));
```

### リサイズ・配置
細かくサイズ変更する機能は力技感ありますが
一つのキーバインドでサイクルできるのは便利です。

```js
// m+shift    .. 最大化
slate.bind(util.key('m', 'shift'), function(win) {
  if (!win) return;
  var bounds = win.screen().visibleRect();
  win.doOperation('move', bounds);
});

// n+shift    .. タテ分割1/2サイズにする
slate.bind(util.key('n', 'shift'), slate.operation('chain', {
  operations: _.map(['left', 'right'], function(d) {
    return slate.operation('push', {
      direction: d,
      style: 'bar-resize:screenSizeX/2'
    });
  })
}));

// o+shift    .. 1/4サイズにする
slate.bind(util.key('o', 'shift'), slate.operation('chain', {
  operations: _.map(['top-left', 'top-right', 'bottom-right', 'bottom-left'], function(d) {
    return slate.operation('corner', {
      direction: d,
      width: 'screenSizeX/2',
      height: 'screenSizeY/2'
    });
  })
}));

// b+shift    .. 細かくサイズ変更
slate.bind(util.key('b', 'shift'), slate.operation('chain', {
  operations: _.map([
      {direction: 'left',  style: 'bar-resize:screenSizeX/3'},
      {direction: 'left',  style: 'bar-resize:screenSizeX/2'},
      {direction: 'left',  style: 'bar-resize:screenSizeX*2/3'},
      {direction: 'left',  style: 'bar-resize:screenSizeX'},
      {direction: 'right', style: 'bar-resize:screenSizeX*2/3'},
      {direction: 'right', style: 'bar-resize:screenSizeX/2'},
      {direction: 'right', style: 'bar-resize:screenSizeX/3'},
      {direction: 'right', style: 'bar-resize:screenSizeX/2'},
      {direction: 'right', style: 'bar-resize:screenSizeX*2/3'},
      {direction: 'left',  style: 'bar-resize:screenSizeX'},
      {direction: 'left',  style: 'bar-resize:screenSizeX*2/3'},
      {direction: 'left',  style: 'bar-resize:screenSizeX/2'}
  ], function(d) {
    return slate.operation('push', d);
  })
}));
```

### フォーカス

```js
// スクリーン内のウィンドウをフォーカス
slate.bind(util.key('h'), slate.operation('focus', { direction: 'left'  }));
slate.bind(util.key('j'), slate.operation('focus', { direction: 'down'  }));
slate.bind(util.key('k'), slate.operation('focus', { direction: 'up'    }));
slate.bind(util.key('l'), slate.operation('focus', { direction: 'right' }));
```

### ウィンドウ移動

```js
// a+shift   .. ウィンドウを左に移動
slate.bind(util.key('a', 'shift'), function(win) {
    if (!win) return;
    var rect = win.rect();
    var bounds = win.screen().visibleRect();
    rect.x -= bounds.width * 0.05;
    win.doOperation('move', rect);
});

// d+shift   .. ウィンドウを右に移動
slate.bind(util.key('d', 'shift'), function(win) {
    if (!win) return;
    var rect = win.rect();
    var bounds = win.screen().visibleRect();
    rect.x += bounds.width * 0.05;
    win.doOperation('move', rect);
});

// w+shift   .. ウィンドウを上に移動
slate.bind(util.key('w', 'shift'), function(win) {
    if (!win) return;
    var rect = win.rect();
    var bounds = win.screen().visibleRect();
    rect.y -= bounds.height * 0.05;
    win.doOperation('move', rect);
});

// s+shift   .. ウィンドウを下に移動
slate.bind(util.key('s', 'shift'), function(win) {
    if (!win) return;
    var rect = win.rect();
    var bounds = win.screen().visibleRect();
    rect.y += bounds.height * 0.05;
    win.doOperation('move', rect);
});

// f+shift    .. 次のスクリーンへ飛ばす
//slate.bind(util.key('f', 'shift'), function(win) {
//  if (!win) return;
//  var next = util.nextScreen(win.screen());
//  win.move(next.visibleRect());
//});
```

### ウィンドウ拡大・縮小

```js
// h+shift   .. ウィンドウが左にあるなら縮小, 右にあるなら拡大
slate.bind(util.key('h', 'shift'), function(win) {
  if (!win) return;
  var rect = win.rect();
  var bounds = win.screen().visibleRect();
  if (bounds.x + bounds.width - 30 < rect.x + rect.width) {
    rect.x -= bounds.width * 0.05;
    rect.width = bounds.x + bounds.width - rect.x;
  } else {
    rect.width -= bounds.width * 0.05;
  }
  win.doOperation('move', rect);
});

// l+shift   .. ウィンドウが右にあるなら縮小, 左にあるなら拡大
slate.bind(util.key('l', 'shift'), function(win) {
  if (!win) return;
  var rect = win.rect();
  var bounds = win.screen().visibleRect();
  if (rect.x < bounds.x + 30) {
    rect.x = bounds.x;
    rect.width += bounds.width * 0.05;
  } else {
    rect.x += bounds.width * 0.05;
    rect.width -= bounds.width * 0.05;
  }
  win.doOperation('move', rect);
});

// k+shift   .. ウィンドウが上にあるなら縮小, 下にあるなら拡大
slate.bind(util.key('k', 'shift'), function(win) {
  if (!win) return;
  var rect = win.rect();
  var bounds = win.screen().visibleRect();
  if (bounds.y + bounds.height - 30 < rect.y + rect.height) {
    rect.y -= bounds.height * 0.05;
    rect.height += bounds.height * 0.05;
  } else {
    rect.height -= bounds.height * 0.05;
  }
  win.doOperation('move', rect);
});

// j+shift   .. ウィンドウが下にあるなら縮小, 上にあるなら拡大
slate.bind(util.key('j', 'shift'), function(win) {
  if (!win) return;
  var rect = win.rect();
  var bounds = win.screen().visibleRect();
  if (rect.y < bounds.y + 30) {
    rect.y = bounds.y;
    rect.height += bounds.height * 0.05;
  } else {
    rect.y += bounds.height * 0.05;
    rect.height -= bounds.height * 0.05;
  }
  win.doOperation('move', rect);
});
```

## おわりに
最終更新が2013年とメンテナンスされてないことが気になりますが
設定が柔軟に書けるため Mac をお使いの方にはおすすめです。
(自動で賢くレイアウトしてくれる [ヤバいウィンドウマネージャ](https://github.com/koekeishiya/yabai) もおすすめです)