やあ、みなさん。
SlateというMac専用のオープンソースのウィンドウマネージャを使うと、キーボード生活が楽になります。
jigish/slate - GitHub
Macにおけるウィンドウマネージャ的な役割を担うソフトウェアで有名なものには『BetterTouchTool』や『Spectacle』なんかがあるかと思いますが、それらよりかはかなり細かく設定できると思われて便利です。
設定方法
ホームフォルダに".slate.js"というJavaScriptファイルを置いとくと、それを設定ファイルとして読み込んでくれます。専用の表記方法を利用した".slate"というファイルも置いておけるらしくて、これらはショートカットキーが被ってなければ共存できるらしいです。
何ができるの?
この記事の最後に僕が使っている".slate.js"のソースを載せますが、それを使うと以下のようなことができます。alt-shift-tab | 複数ウィンドウあるアプリを画面にタイル状に並べる |
alt-cmd-t | iTermにフォーカスor起動 |
alt-cmd-e | Emacs〃 |
alt-cmd-b | Firefox〃 |
alt-cmd-f | Finder〃 |
alt-cmd-d | Dictionary〃 |
alt-cmd-i | iTunes〃 |
alt-cmd-space | Found〃 |
alt-cmd-p | Preview〃 |
alt-cmd-m | Mjograph〃 |
alt-h, j, k or l | Vimライクにウィンドウのフォーカスを移動 |
alt-i | 下に隠れているウィンドウをフォーカス |
alt-p | スクリーン間でフォーカスを移動 |
alt-shift-p | 次のスクリーンへ飛ばす |
alt-shift-o | 四隅に飛ばす |
alt-shift-u | 左右に飛ばす |
alt-shift-i | 上下に飛ばす |
alt-shift-h | ウィンドウが左にあるなら縮小, 右にあるなら拡大 |
alt-shift-l | ウィンドウが右にあるなら縮小, 左にあるなら拡大 |
alt-shift-k | ウィンドウが上にあるなら縮小, 下にあるなら拡大 |
alt-shift-j | ウィンドウが下にあるなら縮小, 上にあるなら拡大 |
alt-shift-m | ウィンドウを最大化 |
alt-n | 同じアプリケーションで別のウィンドウにフォーカスする |
alt-cmd系はアプリを起動するものに割り当てて、alt-shift系はウィンドウのリサイズ関係、alt系はウィンドウのフォーカス関係になってます。
alt-ctrlは僕のキーボード配列じゃ打ちづらいんで入れてませんが、入れてもいいと思います。
設定
以下が僕の".slate.js"ファイルです。JavaScript分かんないんで、ほとんどコピペです。
// http://yohasebe.com/wp/archives/3513 // for tiling windows of focused app onto desktop // (2 x 2, clockwise) var topLeft = slate.operation("corner", { "direction" : "top-left", "width" : "screenSizeX/2", "height" : "screenSizeY/2" }); var topRight = slate.operation("corner", { "direction" : "top-right", "width" : "screenSizeX/2", "height" : "screenSizeY/2" }); var bottomRight = slate.operation("corner", { "direction" : "bottom-right", "width" : "screenSizeX/2", "height" : "screenSizeY/2" }); var bottomLeft = slate.operation("corner", { "direction" : "bottom-left", "width" : "screenSizeX/2", "height" : "screenSizeY/2" }); // [tab]+alt+shiftでアプリのウィンドウをタイル状に並べる var tileKey = "tab:alt;shift"; slate.bind(tileKey, function(win){ var appName = win.app().name(); var tiled = {}; tiled[appName] = { "operations" : [topLeft, topRight, bottomRight, bottomLeft], "main-first" : true, "repeat" : true }; var tiledLayout = slate.layout("tiledLayout", tiled); slate.operation("layout", {"name" : tiledLayout }).run(); slate.operation("show", {"app" : appName}).run(); }); // http://d.hatena.ne.jp/sugyan/20130301/1362129310 // アプリ立ち上げる関数 var 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 })); }; }; S.bind('t:alt,cmd', launch_and_focus('iTerm')); S.bind('e:alt,cmd', launch_and_focus('Emacs')); S.bind('b:alt,cmd', launch_and_focus('Firefox')); S.bind('f:alt,cmd', launch_and_focus('Finder')); S.bind('d:alt,cmd', launch_and_focus('Dictionary')); S.bind('i:alt,cmd', launch_and_focus('iTunes')); S.bind('space:alt,cmd', launch_and_focus('Found')); S.bind('p:alt,cmd', launch_and_focus('Preview')); S.bind('m:alt,cmd', launch_and_focus('Mjograph')); // http://www.infiniteloop.co.jp/blog/2013/08/osx_slate/ var util = { // alt + .. key: function(k, mod) { return k + ':alt' + (mod ? ',' + mod : ''); }, focusWindow: function(f) { var hit = false; slate.eachApp(function(app) { if (hit) return; app.eachWindow(function(win) { if (hit) return; if (f(win)) { win.focus(); hit = true; } }); }); }, nextScreen: function(screen) { return slate.screenForRef(String( (screen.id()+1)%slate.screenCount() )); } }; // ----------- 以下 alt+ // hjkl .. その方向へフォーカス移動 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' })); // i .. 下に隠れているウィンドウをフォーカス slate.bind(util.key('i'), slate.operation('focus', { direction: 'behind' })); // p .. スクリーン間でフォーカスを移動 slate.bind(util.key('p'), function(win) { var next = util.nextScreen(slate.screen()); util.focusWindow(function(win) { return win.screen().id() == next.id(); }); }); // p+shift .. 次のスクリーンへ飛ばす slate.bind(util.key('p', 'shift'), function(win) { if (!win) return; var next = util.nextScreen(win.screen()); win.move(next.visibleRect()); }); // o+shift .. 4隅に飛ばす var corners = slate.bind(util.key('o', 'shift'), slate.operation('chain', { operations: _.map(['top-right', 'bottom-right', 'bottom-left', 'top-left'], function(d) { return slate.operation('corner', { direction: d, width: 'screenSizeX/2', height: 'screenSizeY/2' }); }) })); // u+shift .. 左右に飛ばす slate.bind(util.key('u', 'shift'), slate.operation('chain', { operations: _.map(['left', 'right'], function(d) { return slate.operation('push', { direction: d, style: 'bar-resize:screenSizeX/2' }); }) })); // i+shift .. 上下に飛ばす slate.bind(util.key('i', 'shift'), slate.operation('chain', { operations: _.map(['top', 'bottom'], function(d) { return slate.operation('push', { direction: d, style: 'bar-resize:screenSizeY/2' }); }) })); // 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); }); // m .. 最大化 slate.bind(util.key('m', 'shift'), function(win) { if (!win) return; var bounds = win.screen().visibleRect(); win.doOperation('move', bounds); }); // http://mint.hateblo.jp/category/Slate // 同じアプリケーションで別のウィンドウにフォーカスする (Chrome対応版) S.bind('n:alt', function() { function get_next_win(windows) { truth_values_of_is_main = _.map(windows, function(w){ return w.isMain(); }) next_idx = _.indexOf(truth_values_of_is_main, 1) + 1; if (next_idx >= _.size(windows)) { return windows[0]; } return windows[next_idx]; } windows = []; slate.app().eachWindow(function(win){ if (win.title() !== '') { windows.push(win); } // タイトルが無いウィンドウは無視 }); if (_.size(windows) === 1){ return; } sorted = _.sortBy(windows, function(win){ return win.title(); }); get_next_win(sorted).focus(); });設定し終わったらマウスを外に投げましょう。
追記
ウィンドウの微妙な位置調整がやりたい場面があって、その関数がまだ無かったので作りました。Alt + Shift + WASDキーで上下左右の移動ができるようになります。
// 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); }); // 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); }); // 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); }); // 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); });
参考サイト
Slateを入れてみた - すぎゃーんメモSlate - ミントフレーバー緑茶
Slateの設定ファイル(JavaScript版) | yohasebe.com
OS Xで作業効率を5%上げるSlateの紹介 | 株式会社インフィニットループ技術ブログ
0 件のコメント :
コメントを投稿