-
-
Save edinella/9c7acb41d411717d8b04 to your computer and use it in GitHub Desktop.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// Forked from kaushikgopal/.phoenix.js | |
// Released under MIT license - http://opensource.org/licenses/MIT | |
var BROWSER; | |
var EDITOR; | |
var FINDER; | |
var GRID_HEIGHT; | |
var GRID_WIDTH; | |
var MARGIN_X; | |
var MARGIN_Y; | |
var MUSIC; | |
var TERMINAL; | |
var VIDEO; | |
var changeGridHeight; | |
var changeGridWidth; | |
var debug; | |
var forApp; | |
var key_binding; | |
var lastFrames; | |
var layouts; | |
var mash; | |
var moveWindowLeftOneColumn; | |
var moveWindowRightOneColumn; | |
var moveWindowToNextScreen; | |
var moveWindowToPreviousScreen; | |
var snapAllToGrid; | |
var switchLayout; | |
var transposeWindows; | |
var windowDownOneRow; | |
var windowGrowOneGridColumn; | |
var windowGrowOneGridRow; | |
var windowShrinkOneGridColumn; | |
var windowShrinkOneGridRow; | |
var windowToFullHeight; | |
var windowUpOneRow; | |
var tileWindowsForThisApp; | |
debug = function(message) { | |
return api.alert(message, 10); | |
}; | |
MARGIN_X = 3; | |
MARGIN_Y = 3; | |
GRID_WIDTH = 2; | |
GRID_HEIGHT = 2; | |
EDITOR = 'Atom'; | |
BROWSER = 'Chrome'; | |
TERMINAL = 'Terminal'; | |
FINDER = 'Finder'; | |
MUSIC = 'Spotify'; | |
VIDEO = 'Quicktime Player'; | |
layouts = { | |
'Editor and Browser': { | |
0: {app: BROWSER, whereTo: 'toRightHalf'}, | |
1: {app: EDITOR, whereTo: 'toLeftHalf'} | |
}, | |
'Editor and Terminal': { | |
0: {app: TERMINAL, whereTo: 'toRightHalf'}, | |
1: {app: EDITOR, whereTo: 'toLeftHalf'} | |
}, | |
'Terminal and Browser': { | |
0: {app: TERMINAL, whereTo: 'toLeftHalf'}, | |
1: {app: BROWSER, whereTo: 'toRightHalf'} | |
}, | |
'Finder and Terminal': { | |
0: {app: TERMINAL, whereTo: 'toRightHalf'}, | |
1: {app: FINDER, whereTo: 'toLeftHalf'} | |
}, | |
'Finder and Browser': { | |
0: {app: BROWSER, whereTo: 'toRightHalf'}, | |
1: {app: FINDER, whereTo: 'toLeftHalf'} | |
} | |
}; | |
snapAllToGrid = function() { | |
Window.visibleWindows().map(function(win) { | |
win.snapToGrid(); | |
}); | |
}; | |
changeGridWidth = function(by_) { | |
GRID_WIDTH = Math.max(1, GRID_WIDTH + by_); | |
api.alert('grid is now ' + GRID_WIDTH + ' tiles wide', 1); | |
snapAllToGrid(); | |
}; | |
changeGridHeight = function(by_) { | |
GRID_HEIGHT = Math.max(1, GRID_HEIGHT + by_); | |
api.alert('grid is now ' + GRID_HEIGHT + ' tiles high', 1); | |
snapAllToGrid(); | |
}; | |
Window.prototype.getGrid = function() { | |
var winFrame = this.frame(); | |
var screenRect = this.screen().frameWithoutDockOrMenu(); | |
var thirdScreenWidth = screenRect.width / GRID_WIDTH; | |
var halfScreenHeight = screenRect.height / GRID_HEIGHT; | |
return { | |
x: Math.round((winFrame.x - screenRect.x) / thirdScreenWidth), | |
y: Math.round((winFrame.y - screenRect.y) / halfScreenHeight), | |
width: Math.max(1, Math.round(winFrame.width / thirdScreenWidth)), | |
height: Math.max(1, Math.round(winFrame.height / halfScreenHeight)) | |
}; | |
}; | |
Window.prototype.setGrid = function(grid, screen) { | |
var screenRect = screen.frameWithoutDockOrMenu(); | |
var thirdScreenWidth = screenRect.width / GRID_WIDTH; | |
var halfScreenHeight = screenRect.height / GRID_HEIGHT; | |
var newFrame = { | |
x: (grid.x * thirdScreenWidth) + screenRect.x, | |
y: (grid.y * halfScreenHeight) + screenRect.y, | |
width: grid.width * thirdScreenWidth, | |
height: grid.height * halfScreenHeight | |
}; | |
newFrame.x += MARGIN_X; | |
newFrame.y += MARGIN_Y; | |
newFrame.width -= MARGIN_X * 2.0; | |
newFrame.height -= MARGIN_Y * 2.0; | |
return this.setFrame(newFrame); | |
}; | |
Window.prototype.snapToGrid = function() { | |
if (this.isNormalWindow()) { | |
return this.setGrid(this.getGrid(), this.screen()); | |
} | |
}; | |
Window.prototype.calculateGrid = function(x, y, width, height) { | |
var screen; | |
screen = this.screen().frameWithoutDockOrMenu(); | |
return { | |
x: Math.round(x * screen.width) + MARGIN_X + screen.x, | |
y: Math.round(y * screen.height) + MARGIN_Y + screen.y, | |
width: Math.round(width * screen.width) - 2.0 * MARGIN_X, | |
height: Math.round(height * screen.height) - 2.0 * MARGIN_Y | |
}; | |
}; | |
Window.prototype.toGrid = function(x, y, width, height) { | |
var rect = this.calculateGrid(x, y, width, height); | |
this.setFrame(rect); | |
return this; | |
}; | |
Window.prototype.topRight = function() { | |
var f = this.frame(); | |
return { | |
x: f.x + f.width, | |
y: f.y | |
}; | |
}; | |
Window.prototype.toLeft = function() { | |
var p = this.topLeft(); | |
return _.chain(this.windowsToWest()).filter(function(win) { | |
return win.topLeft().x < p.x - 10; | |
}).value(); | |
}; | |
Window.prototype.toRight = function() { | |
var p = this.topRight(); | |
return _.chain(this.windowsToEast()).filter(function(win) { | |
return win.topRight().x > p.x + 10; | |
}).value(); | |
}; | |
Window.prototype.info = function() { | |
var f = this.frame(); | |
return [ | |
'[', this.app().pid, '] ', this.app().title(), ' : ', this.title(), '\n', | |
'{x:', f.x, ', y:', f.y, ', width:', f.width, ', height:', f.height, '}\n', | |
].join(''); | |
}; | |
Window.sortByMostRecent = function(windows) { | |
var allVisible = Window.visibleWindowsMostRecentFirst(); | |
return _.chain(windows).sortBy(function(win) { | |
return _.map(allVisible, function(w) { | |
return w.info(); | |
}).indexOf(win.info()); | |
}).value(); | |
}; | |
lastFrames = {}; | |
Window.prototype.toFullScreen = function() { | |
var fullFrame = this.calculateGrid(0, 0, 1, 1); | |
if (!_.isEqual(this.frame(), fullFrame)) { | |
this.rememberFrame(); | |
return this.toGrid(0, 0, 1, 1); | |
} else if (lastFrames[this]) { | |
this.setFrame(lastFrames[this]); | |
return this.forgetFrame(); | |
} | |
}; | |
Window.prototype.rememberFrame = function() { | |
return (lastFrames[this] = this.frame()); | |
}; | |
Window.prototype.forgetFrame = function() { | |
return delete lastFrames[this]; | |
}; | |
Window.prototype.toTopHalf = function() { | |
return this.toGrid(0, 0, 1, 0.5); | |
}; | |
Window.prototype.toBottomHalf = function() { | |
return this.toGrid(0, 0.5, 1, 0.5); | |
}; | |
Window.prototype.toLeftHalf = function() { | |
return this.toGrid(0, 0, 0.5, 1); | |
}; | |
Window.prototype.toRightHalf = function() { | |
return this.toGrid(0.5, 0, 0.5, 1); | |
}; | |
Window.prototype.toTopRight = function() { | |
return this.toGrid(0.5, 0, 0.5, 0.5); | |
}; | |
Window.prototype.toBottomRight = function() { | |
return this.toGrid(0.5, 0.5, 0.5, 0.5); | |
}; | |
Window.prototype.toTopLeft = function() { | |
return this.toGrid(0, 0, 0.5, 0.5); | |
}; | |
Window.prototype.toBottomLeft = function() { | |
return this.toGrid(0, 0.5, 0.5, 0.5); | |
}; | |
moveWindowToNextScreen = function() { | |
var win = Window.focusedWindow(); | |
return win.setGrid(win.getGrid(), win.screen().nextScreen()); | |
}; | |
moveWindowToPreviousScreen = function() { | |
var win = Window.focusedWindow(); | |
return win.setGrid(win.getGrid(), win.screen().previousScreen()); | |
}; | |
moveWindowLeftOneColumn = function() { | |
var win = Window.focusedWindow(); | |
var frame = win.getGrid(); | |
frame.x = Math.max(frame.x - 1, 0); | |
return win.setGrid(frame, win.screen()); | |
}; | |
moveWindowRightOneColumn = function() { | |
var win = Window.focusedWindow(); | |
var frame = win.getGrid(); | |
frame.x = Math.min(frame.x + 1, GRID_WIDTH - frame.width); | |
return win.setGrid(frame, win.screen()); | |
}; | |
windowGrowOneGridColumn = function() { | |
var win = Window.focusedWindow(); | |
var frame = win.getGrid(); | |
frame.width = Math.min(frame.width + 1, GRID_WIDTH - frame.x); | |
return win.setGrid(frame, win.screen()); | |
}; | |
windowShrinkOneGridColumn = function() { | |
var win = Window.focusedWindow(); | |
var frame = win.getGrid(); | |
frame.width = Math.max(frame.width - 1, 1); | |
return win.setGrid(frame, win.screen()); | |
}; | |
windowGrowOneGridRow = function() { | |
var win = Window.focusedWindow(); | |
var frame = win.getGrid(); | |
frame.height = Math.min(frame.height + 1, GRID_HEIGHT); | |
return win.setGrid(frame, win.screen()); | |
}; | |
windowShrinkOneGridRow = function() { | |
var win = Window.focusedWindow(); | |
var frame = win.getGrid(); | |
frame.height = Math.max(frame.height - 1, 1); | |
return win.setGrid(frame, win.screen()); | |
}; | |
windowDownOneRow = function() { | |
var win = Window.focusedWindow(); | |
var frame = win.getGrid(); | |
frame.y = Math.min(Math.floor(frame.y + 1), GRID_HEIGHT - 1); | |
return win.setGrid(frame, win.screen()); | |
}; | |
windowUpOneRow = function() { | |
var win = Window.focusedWindow(); | |
var frame = win.getGrid(); | |
frame.y = Math.max(Math.floor(frame.y - 1), 0); | |
return win.setGrid(frame, win.screen()); | |
}; | |
windowToFullHeight = function() { | |
var win = Window.focusedWindow(); | |
var frame = win.getGrid(); | |
frame.y = 0; | |
frame.height = GRID_HEIGHT; | |
return win.setGrid(frame, win.screen()); | |
}; | |
transposeWindows = function(swapFrame, switchFocus) { | |
if (swapFrame === null) { | |
swapFrame = true; | |
} | |
if (switchFocus === null) { | |
switchFocus = true; | |
} | |
var win = Window.focusedWindow(); | |
var left = win.toRight(); | |
var right = win.toLeft(); | |
var targets = left.length > 0 ? left : right.length > 0 ? right : void 0; | |
var hasWinToTranspose = (targets ? targets.length : void 0) > 0; | |
if (!hasWinToTranspose) { | |
api.alert('Can\'t see any windows to transpose'); | |
return; | |
} | |
target = Window.sortByMostRecent(targets)[0]; | |
var tFrame = target.frame(); | |
var wFrame = win.frame(); | |
if (swapFrame) { | |
win.setFrame(tFrame); | |
target.setFrame(wFrame); | |
} else { | |
target.topLeft({ | |
x: wFrame.x, | |
y: wFrame.y | |
}); | |
win.topLeft({ | |
x: tFrame.x, | |
y: tFrame.y | |
}); | |
} | |
if (switchFocus) { | |
return target.focusWindow(); | |
} | |
}; | |
Window.prototype.tileWindowsForThisApp = function() { | |
var fullFrame = this.calculateGrid(0, 0, 1, 1); | |
var title = this.app().title(); | |
var prevY = 0; | |
var prevX = 0; | |
var newFrame; | |
var currentAppWindows = _(Window.visibleWindows()).filter(function(window) { | |
if (window.app().title() == title) { | |
return true; | |
} | |
}); | |
currentAppWindows.reverse(); | |
_.each(currentAppWindows, function(window) { | |
newFrame = { | |
x: prevX, | |
y: prevY, | |
width: 0.8 * fullFrame.width, | |
height: 0.8 * fullFrame.height | |
}; | |
window.setFrame(newFrame); | |
window.focusWindow(); | |
prevY = prevY + 100; | |
prevX = prevX + 50; | |
if (prevY + 0.8 * fullFrame.height >= fullFrame.height) { | |
prevY = 0; | |
} | |
if (prevX + 0.8 * fullFrame.width >= fullFrame.width) { | |
prevX = 0; | |
} | |
}); | |
}; | |
App.prototype.firstWindow = function() { | |
return this.visibleWindows()[0]; | |
}; | |
App.byTitle = function(title) { | |
var apps = this.runningApps(); | |
var i = 0; | |
while (i < apps.length) { | |
var app = apps[i]; | |
if (app.title() === title) { | |
app.show(); | |
return app; | |
} | |
i++; | |
} | |
}; | |
App.allWithTitle = function(title) { | |
return _(this.runningApps()).filter(function(app) { | |
if (app.title() === title) { | |
return true; | |
} | |
}); | |
}; | |
App.focusOrStart = function(title) { | |
var apps = App.allWithTitle(title); | |
if (_.isEmpty(apps)) { | |
api.alert('Attempting to start ' + title); | |
api.launch(title); | |
return; | |
} | |
var windows = _.chain(apps).map(function(x) { | |
return x.allWindows(); | |
}).flatten().value(); | |
var activeWindows = _(windows).reject(function(win) { | |
return win.isWindowMinimized(); | |
}); | |
if (_.isEmpty(activeWindows)) { | |
api.launch(title); | |
} | |
activeWindows.forEach(function(win) { | |
win.focusWindow(); | |
}); | |
}; | |
forApp = function(title, fn) { | |
var app; | |
app = App.byTitle(title); | |
if (app) { | |
return _.each(app.visibleWindows(), fn); | |
} | |
}; | |
switchLayout = function(name) { | |
return _.each(layouts[name], function(config) { | |
var app; | |
App.focusOrStart(config.app); | |
app = App.byTitle(config.app); | |
return app.firstWindow()[config.whereTo](); | |
}); | |
}; | |
// KEY BINDINGS | |
kBind = function(key, modifier, fn) { | |
return api.bind(key, modifier, fn); | |
}; | |
mash = 'cmd+alt+ctrl'.split('+'); | |
kBind('space', mash, function() { | |
return Window.focusedWindow().toFullScreen(); | |
}); | |
kBind('up', mash, function() { | |
return Window.focusedWindow().toTopHalf(); | |
}); | |
kBind('down', mash, function() { | |
return Window.focusedWindow().toBottomHalf(); | |
}); | |
kBind('left', mash, function() { | |
return Window.focusedWindow().toLeftHalf(); | |
}); | |
kBind('right', mash, function() { | |
return Window.focusedWindow().toRightHalf(); | |
}); | |
kBind('\\', mash, function() { | |
return moveWindowToNextScreen(); | |
}); | |
kBind('P', mash, function() { | |
return moveWindowToPreviousScreen(); | |
}); | |
kBind('Y', mash, function() { | |
return transposeWindows(true, true); | |
}); | |
kBind('Q', mash, function() { | |
return Window.focusedWindow().toTopLeft(); | |
}); | |
kBind('A', mash, function() { | |
return Window.focusedWindow().toBottomLeft(); | |
}); | |
kBind('W', mash, function() { | |
return Window.focusedWindow().toTopRight(); | |
}); | |
kBind('S', mash, function() { | |
return Window.focusedWindow().toBottomRight(); | |
}); | |
kBind('R', mash, function() { | |
return Window.focusedWindow().focusWindowUp(); | |
}); | |
kBind('D', mash, function() { | |
return Window.focusedWindow().focusWindowLeft(); | |
}); | |
kBind('F', mash, function() { | |
return Window.focusedWindow().focusWindowRight(); | |
}); | |
kBind('C', mash, function() { | |
return Window.focusedWindow().focusWindowDown(); | |
}); | |
kBind('O', mash, function() { | |
return changeGridHeight(+1); | |
}); | |
kBind('I', mash, function() { | |
return changeGridHeight(-1); | |
}); | |
kBind('K', mash, function() { | |
return changeGridWidth(+1); | |
}); | |
kBind('L', mash, function() { | |
return changeGridWidth(-1); | |
}); | |
kBind('=', mash, function() { | |
return windowShrinkOneGridRow(); | |
}); | |
kBind('-', mash, function() { | |
return windowGrowOneGridRow(); | |
}); | |
kBind(']', mash, function() { | |
return windowShrinkOneGridColumn(); | |
}); | |
kBind('[', mash, function() { | |
return windowGrowOneGridColumn(); | |
}); | |
kBind(';', mash, function() { | |
return Window.focusedWindow().snapToGrid(); | |
}); | |
kBind('\'', mash, function() { | |
return Window.visibleWindows().map(function(win) {return win.snapToGrid();}); | |
}); | |
kBind('T', mash, function() { | |
return Window.focusedWindow().tileWindowsForThisApp(); | |
}); | |
kBind('U', mash, function() { | |
return windowToFullHeight(); | |
}); |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment