Last active
January 23, 2016 22:38
-
-
Save kaushikgopal/5894b44320efc8c5cb18 to your computer and use it in GitHub Desktop.
Kaushik Gopal's Phoenix.Js window management script
This file contains 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
// Generated by CoffeeScript 1.10.0 | |
(function() { | |
var EMACS, FINDER, GRID_HEIGHT, GRID_WIDTH, ITERM, MARGIN_X, MARGIN_Y, TERMINAL, VIM, changeGridHeight, changeGridWidth, debug, focused, key_binding, keys, lastFrames, mash, moveWindowToNextScreen, moveWindowToPreviousScreen, snapAllToGrid, windowDownOneRow, windowGrowOneGridColumn, windowGrowOneGridRow, windowLeftOneColumn, windowRightOneColumn, windowShrinkOneGridColumn, windowShrinkOneGridRow, windowToFullHeight, windowUpOneRow, windows; | |
debug = function(o, label) { | |
if (label == null) { | |
label = "obj: "; | |
} | |
Phoenix.log(label); | |
return Phoenix.log(JSON.stringify(o)); | |
}; | |
MARGIN_X = 3; | |
MARGIN_Y = 3; | |
GRID_WIDTH = 20; | |
GRID_HEIGHT = 16; | |
_.mixin({ | |
flatmap: function(list, iteratee, context) { | |
return _.flatten(_.map(list, iteratee, context)); | |
} | |
}); | |
focused = function() { | |
return Window.focusedWindow(); | |
}; | |
windows = function() { | |
return Window.visibleWindows(); | |
}; | |
Window.prototype.screenRect = function() { | |
return this.screen().visibleFrameInRectangle(); | |
}; | |
Window.prototype.fullGridFrame = function() { | |
return this.calculateGrid({ | |
y: 0, | |
x: 0, | |
width: 1, | |
height: 1 | |
}); | |
}; | |
snapAllToGrid = function() { | |
return _.map(visible(), function(win) { | |
return win.snapToGrid(); | |
}); | |
}; | |
changeGridWidth = function(n) { | |
GRID_WIDTH = Math.max(1, GRID_WIDTH + n); | |
Phoenix.notify("grid is " + GRID_WIDTH + " tiles wide"); | |
return snapAllToGrid(); | |
}; | |
changeGridHeight = function(n) { | |
GRID_HEIGHT = Math.max(1, GRID_HEIGHT + n); | |
Phoenix.notify("grid is " + GRID_HEIGHT + " tiles high"); | |
return snapAllToGrid(); | |
}; | |
Window.prototype.getGrid = function() { | |
var frame, gridHeight, gridWidth; | |
frame = this.frame(); | |
gridWidth = this.screenRect().width / GRID_WIDTH; | |
gridHeight = this.screenRect().height / GRID_HEIGHT; | |
return { | |
y: Math.round((frame.y - this.screenRect().y) / gridHeight), | |
x: Math.round((frame.x - this.screenRect().x) / gridWidth), | |
width: Math.max(1, Math.round(frame.width / gridWidth)), | |
height: Math.max(1, Math.round(frame.height / gridHeight)) | |
}; | |
}; | |
Window.prototype.setGrid = function(grid, screen) { | |
var gridHeight, gridWidth; | |
gridWidth = this.screenRect().width / GRID_WIDTH; | |
gridHeight = this.screenRect().height / GRID_HEIGHT; | |
return this.setFrame({ | |
y: ((grid.y * gridHeight) + this.screenRect().y) + MARGIN_Y, | |
x: ((grid.x * gridWidth) + this.screenRect().x) + MARGIN_X, | |
width: (grid.width * gridWidth) - (MARGIN_X * 2.0), | |
height: (grid.height * gridHeight) - (MARGIN_Y * 2.0) | |
}); | |
}; | |
Window.prototype.snapToGrid = function() { | |
if (this.isNormal()) { | |
return this.setGrid(this.getGrid(), this.screen()); | |
} | |
}; | |
Window.prototype.calculateGrid = function(arg) { | |
var height, width, x, y; | |
x = arg.x, y = arg.y, width = arg.width, height = arg.height; | |
return { | |
y: Math.round(y * this.screenRect().height) + MARGIN_Y + this.screenRect().y, | |
x: Math.round(x * this.screenRect().width) + MARGIN_X + this.screenRect().x, | |
width: Math.round(width * this.screenRect().width) - 2.0 * MARGIN_X, | |
height: Math.round(height * this.screenRect().height) - 2.0 * MARGIN_Y | |
}; | |
}; | |
Window.prototype.toGrid = function(arg) { | |
var height, rect, width, x, y; | |
x = arg.x, y = arg.y, width = arg.width, height = arg.height; | |
rect = this.calculateGrid({ | |
x: x, | |
y: y, | |
width: width, | |
height: height | |
}); | |
this.setFrame(rect); | |
return this; | |
}; | |
Window.prototype.topRight = function() { | |
return { | |
x: this.frame().x + this.frame().width, | |
y: this.frame().y | |
}; | |
}; | |
Window.prototype.toLeft = function() { | |
return _.filter(this.windowsToWest(), function(win) { | |
return win.topLeft().x < this.topLeft().x - 10; | |
}); | |
}; | |
Window.prototype.toRight = function() { | |
return _.filter(this.windowsToEast(), function(win) { | |
return win.topRight().x > this.topRight().x + 10; | |
}); | |
}; | |
Window.prototype.info = function() { | |
var f; | |
f = this.frame(); | |
return "[" + (this.app().processIdentifier()) + "] " + (this.app().name()) + " : " + (this.title()) + "\n{x:" + f.x + ", y:" + f.y + ", width:" + f.width + ", height:" + f.height + "}\n"; | |
}; | |
Window.sortByMostRecent = function(windows) { | |
var allVisible; | |
allVisible = visibleInOrder(); | |
return _.sortBy(windows, function(win) { | |
return _.map(allVisible, function(w) { | |
return w.info(); | |
}).indexOf(win.info()); | |
}); | |
}; | |
lastFrames = {}; | |
Window.prototype.toFullScreen = function() { | |
if (!_.isEqual(this.frame(), this.fullGridFrame())) { | |
this.rememberFrame(); | |
return this.toGrid({ | |
y: 0, | |
x: 0, | |
width: 1, | |
height: 1 | |
}); | |
} else if (lastFrames[this.uid()]) { | |
this.setFrame(lastFrames[this.uid()]); | |
return this.forgetFrame(); | |
} | |
}; | |
Window.prototype.uid = function() { | |
return (this.app().name()) + "::" + (this.title()); | |
}; | |
Window.prototype.rememberFrame = function() { | |
return lastFrames[this.uid()] = this.frame(); | |
}; | |
Window.prototype.forgetFrame = function() { | |
return delete lastFrames[this.uid()]; | |
}; | |
Window.prototype.toTopHalf = function() { | |
return this.toGrid({ | |
x: 0, | |
y: 0, | |
width: 1, | |
height: 0.5 | |
}); | |
}; | |
Window.prototype.toBottomHalf = function() { | |
return this.toGrid({ | |
x: 0, | |
y: 0.5, | |
width: 1, | |
height: 0.5 | |
}); | |
}; | |
Window.prototype.toLeftHalf = function() { | |
return this.toGrid({ | |
x: 0, | |
y: 0, | |
width: 0.5, | |
height: 1 | |
}); | |
}; | |
Window.prototype.toRightHalf = function() { | |
return this.toGrid({ | |
x: 0.5, | |
y: 0, | |
width: 0.5, | |
height: 1 | |
}); | |
}; | |
Window.prototype.toTopRight = function() { | |
return this.toGrid({ | |
x: 0.5, | |
y: 0, | |
width: 0.5, | |
height: 0.5 | |
}); | |
}; | |
Window.prototype.toBottomRight = function() { | |
return this.toGrid({ | |
x: 0.5, | |
y: 0.5, | |
width: 0.5, | |
height: 0.5 | |
}); | |
}; | |
Window.prototype.toTopLeft = function() { | |
return this.toGrid({ | |
x: 0, | |
y: 0, | |
width: 0.5, | |
height: 0.5 | |
}); | |
}; | |
Window.prototype.toBottomLeft = function() { | |
return this.toGrid({ | |
x: 0, | |
y: 0.5, | |
width: 0.5, | |
height: 0.5 | |
}); | |
}; | |
Window.prototype.pushLeft = function() { | |
return this.toGrid({ | |
x: 0, | |
y: 0, | |
width: 2/3, | |
height: 1 | |
}); | |
}; | |
Window.prototype.pushRight = function() { | |
return this.toGrid({ | |
x: 2/3, | |
y: 0, | |
width: 1/3, | |
height: 1 | |
}); | |
}; | |
Window.prototype.fillToScreenHeight = function() { | |
var fullFrame = this.calculateGrid({ | |
y: 0, | |
x: 0, | |
width: 1, | |
height: 1 | |
}); | |
var newFrame = this.frame(); | |
newFrame.y = 0; | |
newFrame.height = fullFrame.height; | |
Window.focusedWindow().setFrame(newFrame); | |
return this; | |
}; | |
Window.prototype.tileWindowsForThisApp = function() { | |
var fullFrame = this.calculateGrid({ | |
y: 0, | |
x: 0, | |
width: 1, | |
height: 1 | |
}); | |
var name = this.app().name(); | |
var prevY = 0; | |
var prevX = 0; | |
var newFrame; | |
var currentAppWindows = _(Window.visibleWindows()).filter(function(window) { | |
if (window.app().name() == name) { | |
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.focus(); | |
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; | |
}; | |
}); | |
return this; | |
}; | |
moveWindowToNextScreen = function() { | |
return focused().setGrid(focused().getGrid(), focused().screen().nextScreen()); | |
}; | |
moveWindowToPreviousScreen = function() { | |
return focused().setGrid(focused().getGrid(), focused().screen().previousScreen()); | |
}; | |
windowLeftOneColumn = function() { | |
var frame; | |
frame = focused().getGrid(); | |
frame.x = Math.max(frame.x - 1, 0); | |
return focused().setGrid(frame, focused().screen()); | |
}; | |
windowDownOneRow = function() { | |
var frame; | |
frame = focused().getGrid(); | |
frame.y = Math.min(Math.floor(frame.y + 1), GRID_HEIGHT - 1); | |
return focused().setGrid(frame, focused().screen()); | |
}; | |
windowUpOneRow = function() { | |
var frame; | |
frame = focused().getGrid(); | |
frame.y = Math.max(Math.floor(frame.y - 1), 0); | |
return focused().setGrid(frame, focused().screen()); | |
}; | |
windowRightOneColumn = function() { | |
var frame; | |
frame = focused().getGrid(); | |
frame.x = Math.min(frame.x + 1, GRID_WIDTH - frame.width); | |
return focused().setGrid(frame, focused().screen()); | |
}; | |
windowGrowOneGridColumn = function() { | |
var frame; | |
frame = focused().getGrid(); | |
frame.width = Math.min(frame.width + 1, GRID_WIDTH - frame.x); | |
return focused().setGrid(frame, focused().screen()); | |
}; | |
windowShrinkOneGridColumn = function() { | |
var frame; | |
frame = focused().getGrid(); | |
frame.width = Math.max(frame.width - 1, 1); | |
return focused().setGrid(frame, focused().screen()); | |
}; | |
windowGrowOneGridRow = function() { | |
var frame; | |
frame = focused().getGrid(); | |
frame.height = Math.min(frame.height + 1, GRID_HEIGHT); | |
return focused().setGrid(frame, focused().screen()); | |
}; | |
windowShrinkOneGridRow = function() { | |
var frame; | |
frame = focused().getGrid(); | |
frame.height = Math.max(frame.height - 1, 1); | |
return focused().setGrid(frame, focused().screen()); | |
}; | |
windowToFullHeight = function() { | |
var frame; | |
frame = focused().getGrid(); | |
frame.y = 0; | |
frame.height = GRID_HEIGHT; | |
return focused().setGrid(frame, focused().screen()); | |
}; | |
App.prototype.firstWindow = function() { | |
return this.visibleWindows()[0]; | |
}; | |
App.allWithName = function(name) { | |
return _.filter(App.runningApps(), function(app) { | |
return app.name() === name; | |
}); | |
}; | |
App.byName = function(name) { | |
var app; | |
app = _.first(App.allWithName(name)); | |
app.show(); | |
return app; | |
}; | |
App.focusOrStart = function(name) { | |
var activeWindows, apps; | |
apps = App.allWithName(name); | |
if (_.isEmpty(apps)) { | |
Phoenix.notify("Starting " + name); | |
App.launch(name); | |
} else { | |
Phoenix.notify("Switching to " + name); | |
} | |
windows = _.flatmap(apps, function(x) { | |
return x.windows(); | |
}); | |
activeWindows = _.reject(windows, function(win) { | |
return win.isMinimized(); | |
}); | |
if (_.isEmpty(activeWindows)) { | |
App.launch(name); | |
} | |
return _.each(activeWindows, function(win) { | |
return win.focus(); | |
}); | |
}; | |
keys = []; | |
key_binding = function(key, description, modifier, fn) { | |
return keys.push(Phoenix.bind(key, modifier, fn)); | |
}; | |
mash = 'cmd-alt-shift'.split('-'); | |
key_binding('up', 'To Top Half', mash, function() { | |
return Window.focusedWindow().toTopHalf(); | |
}); | |
key_binding('down', 'To Bottom Half', mash, function() { | |
return Window.focusedWindow().toBottomHalf(); | |
}); | |
key_binding('left', 'To Left Half', mash, function() { | |
return Window.focusedWindow().toLeftHalf(); | |
}); | |
key_binding('right', 'To Right Half', mash, function() { | |
return Window.focusedWindow().toRightHalf(); | |
}); | |
key_binding('T', 'Top Left', mash, function() { | |
return Window.focusedWindow().toTopLeft(); | |
}); | |
key_binding('G', 'Bottom Left', mash, function() { | |
return Window.focusedWindow().toBottomLeft(); | |
}); | |
key_binding('Y', 'Top Right', mash, function() { | |
return Window.focusedWindow().toTopRight(); | |
}); | |
key_binding('H', 'Bottom Right', mash, function() { | |
return Window.focusedWindow().toBottomRight(); | |
}); | |
key_binding('[', 'Left Major', mash, function() { | |
return Window.focusedWindow().pushLeft(); | |
}); | |
key_binding(']', 'Right Minor', mash, function() { | |
return Window.focusedWindow().pushRight(); | |
}); | |
key_binding('space', 'Maximize Window', mash, function() { | |
return Window.focusedWindow().toFullScreen(); | |
}); | |
key_binding('W', 'Tile all windows for this app', mash, function() { | |
return Window.focusedWindow().tileWindowsForThisApp(); | |
}); | |
key_binding('U', 'Tile all windows for this app', mash, function() { | |
return Window.focusedWindow().fillToScreenHeight(); | |
}); | |
ITERM = "iTerm2"; | |
VIM = "MacVim"; | |
EMACS = "Emacs"; | |
TERMINAL = "iTerm2"; | |
FINDER = "Finder"; | |
// key_binding('E', 'Launch Emacs', mash, function() { | |
// return App.focusOrStart(EMACS); | |
// }); | |
key_binding('A', 'Launch Atom', mash, function() { | |
return App.focusOrStart("Atom"); | |
}); | |
// key_binding('V', 'Launch Vim', mash, function() { | |
// return App.focusOrStart(VIM); | |
// }); | |
key_binding('I', 'Launch iTerm', mash, function() { | |
return App.focusOrStart("iTerm"); | |
}); | |
// key_binding('C', 'Launch Chrome', mash, function() { | |
// return App.focusOrStart(CHROME); | |
// }); | |
key_binding('F', 'Launch Finder', mash, function() { | |
return App.focusOrStart(FINDER); | |
}); | |
key_binding('P', 'Launch 1Password', mash, function() { | |
return App.focusOrStart("1Password"); | |
}); | |
key_binding('S', 'Launch Spotify', mash, function() { | |
return App.focusOrStart("Spotify"); | |
}); | |
// key_binding('N', 'To Next Screen', mash, function() { | |
// return moveWindowToNextScreen(); | |
// }); | |
// | |
// key_binding('P', 'To Previous Screen', mash, function() { | |
// return moveWindowToPreviousScreen(); | |
// }); | |
// | |
// key_binding('=', 'Increase Grid Columns', mash, function() { | |
// return changeGridWidth(+1); | |
// }); | |
// | |
// key_binding('-', 'Reduce Grid Columns', mash, function() { | |
// return changeGridWidth(-1); | |
// }); | |
// | |
// key_binding('[', 'Increase Grid Rows', mash, function() { | |
// return changeGridHeight(+1); | |
// }); | |
// | |
// key_binding(']', 'Reduce Grid Rows', mash, function() { | |
// return changeGridHeight(-1); | |
// }); | |
// | |
// key_binding(';', 'Snap focused to grid', mash, function() { | |
// return Window.focusedWindow().snapToGrid(); | |
// }); | |
// | |
// key_binding("'", 'Snap all to grid', mash, function() { | |
// return visible().map(function(win) { | |
// return win.snapToGrid(); | |
// }); | |
// }); | |
// | |
// key_binding('H', 'Move Grid Left', mash, function() { | |
// return windowLeftOneColumn(); | |
// }); | |
// | |
// key_binding('J', 'Move Grid Down', mash, function() { | |
// return windowDownOneRow(); | |
// }); | |
// | |
// key_binding('K', 'Move Grid Up', mash, function() { | |
// return windowUpOneRow(); | |
// }); | |
// | |
// key_binding('L', 'Move Grid Right', mash, function() { | |
// return windowRightOneColumn(); | |
// }); | |
// | |
// key_binding('U', 'Window Full Height', mash, function() { | |
// return windowToFullHeight(); | |
// }); | |
// | |
// key_binding('I', 'Shrink by One Column', mash, function() { | |
// return windowShrinkOneGridColumn(); | |
// }); | |
// | |
// key_binding('O', 'Grow by One Column', mash, function() { | |
// return windowGrowOneGridColumn(); | |
// }); | |
// | |
// key_binding(',', 'Shrink by One Row', mash, function() { | |
// return windowShrinkOneGridRow(); | |
// }); | |
// | |
// key_binding('.', 'Grow by One Row', mash, function() { | |
// return windowGrowOneGridRow(); | |
// }); | |
Phoenix.notify("Loaded"); | |
}).call(this); |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment