Skip to content

Instantly share code, notes, and snippets.

@tmandry
Created May 18, 2014 23:48
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save tmandry/e5c79c10166495429f3b to your computer and use it in GitHub Desktop.
Save tmandry/e5c79c10166495429f3b to your computer and use it in GitHub Desktop.
i3-like Phoenix configuration: focusing, fullscreen, marks
// Move windows between monitors, taken from jakemcc's config
function moveToScreen(win, screen) {
if (!screen) {
return;
}
var frame = win.frame();
var oldScreenRect = win.screen().frameWithoutDockOrMenu();
var newScreenRect = screen.frameWithoutDockOrMenu();
var xRatio = newScreenRect.width / oldScreenRect.width;
var yRatio = newScreenRect.height / oldScreenRect.height;
win.setFrame({
x: (Math.round(frame.x - oldScreenRect.x) * xRatio) + newScreenRect.x,
y: (Math.round(frame.y - oldScreenRect.y) * yRatio) + newScreenRect.y,
width: Math.round(frame.width * xRatio),
height: Math.round(frame.height * yRatio)
});
}
function circularLookup(array, index) {
if (index < 0)
return array[array.length + (index % array.length)];
return array[index % array.length];
}
function rotateMonitors(offset) {
var win = Window.focusedWindow();
var currentScreen = win.screen();
var screens = [currentScreen];
for (var x = currentScreen.previousScreen(); x != win.screen(); x = x.previousScreen()) {
screens.push(x);
}
screens = _(screens).sortBy(function(s) { return s.frameWithoutDockOrMenu().x; });
var currentIndex = _(screens).indexOf(currentScreen);
moveToScreen(win, circularLookup(screens, currentIndex + offset));
}
function leftOneMonitor() {
rotateMonitors(-1);
}
function rightOneMonitor() {
rotateMonitors(1);
}
// My additions to Window
Window.prototype.push = function(dir) {
var topLeft = this.topLeft();
var screenFrame = this.screen().frameWithoutDockOrMenu();
if (dir == 'left') {
topLeft.x = screenFrame.x;
} else if (dir == 'right') {
topLeft.x = (screenFrame.x + screenFrame.width) - this.size().width;
} else if (dir == 'top') {
topLeft.y = screenFrame.y;
} else if (dir == 'bottom') {
topLeft.y = (screenFrame.y + screenFrame.height) - this.size().height;
} else {
api.alert("Unknown direction " + dir);
}
this.setTopLeft(topLeft);
return this;
}
Window.prototype.toGrid = function(x, y, width, height) {
var screen = this.screen().frameWithoutDockOrMenu();
var padding = 0;
this.setFrame({
x: Math.round(x * screen.width) + padding + screen.x,
y: Math.round(y * screen.height) + padding + screen.y,
width: Math.round(width * screen.width) - (2 * padding),
height: Math.round(height * screen.height) - (2 * padding)
});
return this;
}
Window.prototype.shift = function(pctX, pctY) {
var topLeft = this.topLeft();
var screen = this.screen().frameWithoutDockOrMenu();
topLeft.x += Math.round(pctX * screen.width);
topLeft.y += Math.round(pctY * screen.height);
this.setTopLeft(topLeft);
}
Window.prototype.resize = function(pctX, pctY, anchorX, anchorY) {
var frame = this.frame();
var screen = this.screen().frameWithoutDockOrMenu();
var adjX = Math.round(pctX * screen.width);
var adjY = Math.round(pctY * screen.height);
frame.width += adjX;
frame.height += adjY;
if (anchorX == 'right') {
frame.x -= adjX;
}
if (anchorY == 'bottom') {
frame.y -= adjY;
}
this.setFrame(frame);
return this;
}
var restoreFrames = {};
Window.prototype.toggleFullscreen = function() {
var screen = this.screen().frameWithoutDockOrMenu();
if (!_.isEqual(this.frame(), screen)) {
restoreFrames[this.title()] = this.frame();
this.setFrame(screen);
} else {
this.setFrame(restoreFrames[this.title()]);
}
}
Window.prototype.forceFocusWindow = function() {
// If the window is on another screen and another window from the same app is on this screen,
// OSX will focus the window on this screen. Once focused, another invocation will go to the
// correct window, however. This forces the correct behavior.
while (!windowEq(Window.focusedWindow(), this)) this.focusWindow();
}
function windowEq(w1, w2) {
return w1.title() == w2.title() && _.isEqual(w1.frame(), w2.frame());
}
// Marks
markBindings = [];
function createMarkBindings() {
NAMES = "abcdefghijklmnopqrstuvwxyz'";
markBindings = _.map(NAMES, function(name) {
binding = api.bind(name, [], function() { markCallback(name); });
return binding;
});
markBindings.push(api.bind('escape', [], cancelMarkOp));
disableMarkBindings();
}
function enableMarkBindings() {
_.each(markBindings, function(binding) { binding.enable(); });
}
function disableMarkBindings() {
_.each(markBindings, function(binding) { binding.disable(); });
}
createMarkBindings();
markOp = null;
marks = {};
function beginMarkOp(opName) {
markOp = opName;
enableMarkBindings();
}
function cancelMarkOp() {
markOp = null;
disableMarkBindings();
}
function markCallback(name) {
disableMarkBindings();
if (markOp == 'name') {
nameMark(name);
} else if (markOp == 'focus') {
focusMark(name);
}
markOp = null;
}
function nameMark(name) {
var win = Window.focusedWindow();
marks[name] = win;
api.alert(win.app().title() + " / " + win.title() + " → " + name, 0.8);
}
function focusMark(name) {
if (name == "'") {
focusLastWindow();
return;
}
mark = marks[name];
if (!mark) return;
mark.forceFocusWindow();
}
function focusLastWindow() {
// TODO: Work around problem of "illegitimate focusings" that happen during forceFocusWindow being
// focused as the last window.
Window.visibleWindowsMostRecentFirst()[1].forceFocusWindow();
}
// Returns the window from the list of windows with the title title, if that title is unique.
// Otherwise, returns undefined.
// function findByUniqueTitle(windows, title) {
// var results = _.find(windows, function(w){ return w.title() == title; });
// if (results.length != 1) return undefined;
// return results[0];
// }
// Window.findByUniqueTitle = function(title) {
// return findByUniqueTitle(Window.allWindows(), title);
// }
// function nameMark(name) {
// var win = Window.focusedWindow();
// marks[name] = {
// app: win.app().title(),
// title: win.title(),
// topLeft: win.topLeft(),
// size: win.size()
// };
// api.alert(name + ": " + win.app().title() + " / " + win.title());
// }
// function focusMark(name) {
// mark = marks[name];
// if (!mark) return;
// var app = _.find(App.runningApps(), function(app){ return app.title() == mark.app; });
// if (!app) return;
// var windows = app.allWindows();
// // First try to match by title, then by location, then by window size.
// var result = findByUniqueTitle(windows, mark.title);
// if (!result) result = _.find(windows, function(w){ return _.isEqual(w.topLeft(), mark.topLeft); });
// if (!result) result = _.find(windows, function(w){ return _.isEqual(w.size(), mark.size); });
// if (result) {
// // If the window is on another screen and another window from the same app is on this screen,
// // OSX will focus the window on this screen. Once focused, another invocation will go to the
// // correct window, however. This forces the correct behavior.
// while (!windowEq(Window.focusedWindow(), result)) result.focusWindow();
// }
// }
// Bindings
function win() { return Window.focusedWindow(); }
var big = 0.10;
var sml = 0.05;
// Focus
api.bind('h', ['alt'], function(){ win().focusWindowLeft(); });
api.bind('l', ['alt'], function(){ win().focusWindowRight(); });
api.bind('k', ['alt'], function(){ win().focusWindowUp(); });
api.bind('j', ['alt'], function(){ win().focusWindowDown(); });
// Movement
api.bind('h', ['alt', 'shift', 'cmd', 'ctrl'], leftOneMonitor);
api.bind('l', ['alt', 'shift', 'cmd', 'ctrl'], rightOneMonitor);
api.bind('h', ['alt', 'shift'], function(){ win().shift(-sml, 0); })
api.bind('l', ['alt', 'shift'], function(){ win().shift(+sml, 0); })
api.bind('k', ['alt', 'shift'], function(){ win().shift(0, -sml); })
api.bind('j', ['alt', 'shift'], function(){ win().shift(0, +sml); })
api.bind('h', ['alt', 'shift', 'cmd'], function(){ win().push('left') });
api.bind('l', ['alt', 'shift', 'cmd'], function(){ win().push('right') });
api.bind('k', ['alt', 'shift', 'cmd'], function(){ win().push('top') });
api.bind('j', ['alt', 'shift', 'cmd'], function(){ win().push('bottom') });
api.bind('h', ['alt', 'shift', 'ctrl'], function(){ win().toGrid(0.0, 0.0, 0.5, 1.0) });
api.bind('l', ['alt', 'shift', 'ctrl'], function(){ win().toGrid(0.5, 0.0, 0.5, 1.0) });
api.bind('y', ['alt', 'shift', 'ctrl'], function(){ win().toGrid(0.0, 0.0, 0.5, 0.5) });
api.bind('p', ['alt', 'shift', 'ctrl'], function(){ win().toGrid(0.5, 0.0, 0.5, 0.5) });
api.bind('.', ['alt', 'shift', 'ctrl'], function(){ win().toGrid(0.5, 0.5, 0.5, 0.5) });
api.bind('b', ['alt', 'shift', 'ctrl'], function(){ win().toGrid(0.0, 0.5, 0.5, 0.5) });
// Resizing
// top/left edges
api.bind('left', ['alt', 'ctrl'], function(){ win().resize(+big, 0.0, 'right', 'bottom') });
api.bind('right', ['alt', 'ctrl'], function(){ win().resize(-big, 0.0, 'right', 'bottom') });
api.bind('up', ['alt', 'ctrl'], function(){ win().resize(0.0, +big, 'right', 'bottom') });
api.bind('down', ['alt', 'ctrl'], function(){ win().resize(0.0, -big, 'right', 'bottom') });
api.bind('left', ['alt', 'ctrl', 'shift'], function(){ win().resize(+sml, 0.0, 'right', 'bottom') });
api.bind('right', ['alt', 'ctrl', 'shift'], function(){ win().resize(-sml, 0.0, 'right', 'bottom') });
api.bind('up', ['alt', 'ctrl', 'shift'], function(){ win().resize(0.0, +sml, 'right', 'bottom') });
api.bind('down', ['alt', 'ctrl', 'shift'], function(){ win().resize(0.0, -sml, 'right', 'bottom') });
// bottom/right edges
api.bind('left', ['alt', 'cmd'], function(){ win().resize(-big, 0.0, 'left', 'top') });
api.bind('right', ['alt', 'cmd'], function(){ win().resize(+big, 0.0, 'left', 'top') });
api.bind('up', ['alt', 'cmd'], function(){ win().resize(0.0, -big, 'left', 'top') });
api.bind('down', ['alt', 'cmd'], function(){ win().resize(0.0, +big, 'left', 'top') });
api.bind('left', ['alt', 'cmd', 'shift'], function(){ win().resize(-sml, 0.0, 'left', 'top') });
api.bind('right', ['alt', 'cmd', 'shift'], function(){ win().resize(+sml, 0.0, 'left', 'top') });
api.bind('up', ['alt', 'cmd', 'shift'], function(){ win().resize(0.0, -sml, 'left', 'top') });
api.bind('down', ['alt', 'cmd', 'shift'], function(){ win().resize(0.0, +sml, 'left', 'top') });
// Misc
api.bind('f', ['alt'], function(){ win().toggleFullscreen() });
// Marks
api.bind('m', ['alt'], function(){ beginMarkOp('name'); });
api.bind("'", ['alt'], function(){ beginMarkOp('focus'); });
// TODO
// 'jigsaw' move
// remember mouse position
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment