hydra config
-- make sure Hydra launches at login | |
autolaunch.set(true) | |
-- watch for changes | |
pathwatcher.new(os.getenv("HOME") .. "/.hydra/", hydra.reload):start() | |
-- notify on start | |
notify.show("Hydra", "Started!", "", "") | |
-- extensions | |
ext.frame = {} | |
ext.win = {} | |
-- window margins and positions | |
ext.win.margin = 10 | |
ext.win.positions = {} | |
-- returns frame pushed to screen edge | |
function ext.frame.push(screen, direction) | |
local frames = { | |
[ "up" ] = function() | |
return { | |
x = ext.win.margin + screen.x, | |
y = screen.y, | |
w = screen.w - ext.win.margin * 2, | |
h = screen.h / 2 - ext.win.margin | |
} | |
end, | |
[ "down" ] = function() | |
return { | |
x = ext.win.margin + screen.x, | |
y = ext.win.margin * 3 / 4 + screen.h / 2 + screen.y, | |
w = screen.w - ext.win.margin * 2, | |
h = screen.h / 2 - ext.win.margin * (2 - 1 / 4) | |
} | |
end, | |
[ "left" ] = function() | |
return { | |
x = ext.win.margin + screen.x, | |
y = screen.y, | |
w = screen.w / 2 - ext.win.margin * (2 - 1 / 4), | |
h = screen.h - ext.win.margin * (2 - 1 / 4) | |
} | |
end, | |
[ "right" ] = function() | |
return { | |
x = ext.win.margin / 2 + screen.w / 2 + screen.x, | |
y = screen.y, | |
w = screen.w / 2 - ext.win.margin * (2 - 1 / 4), | |
h = screen.h - ext.win.margin * (2 - 1 / 4) | |
} | |
end | |
} | |
return frames[direction]() | |
end | |
-- returns frame pushed to screen corner | |
function ext.frame.corner(screen, direction) | |
local frames = { | |
[ "up" ] = function() | |
return { | |
x = ext.win.margin + screen.x, | |
y = screen.y, | |
w = screen.w / 2 - ext.win.margin * (2 - 1 / 4), | |
h = screen.h / 2 - ext.win.margin | |
} | |
end, | |
[ "down" ] = function() | |
return { | |
x = ext.win.margin / 2 + screen.w / 2 + screen.x, | |
y = ext.win.margin * 3 / 4 + screen.h / 2 + screen.y, | |
w = screen.w / 2 - ext.win.margin * (2 - 1 / 4), | |
h = screen.h / 2 - ext.win.margin * (2 - 1 / 4) | |
} | |
end, | |
[ "left" ] = function() | |
return { | |
x = ext.win.margin + screen.x, | |
y = ext.win.margin * 3 / 4 + screen.h / 2 + screen.y, | |
w = screen.w / 2 - ext.win.margin * (2 - 1 / 4), | |
h = screen.h / 2 - ext.win.margin * (2 - 1 / 4) | |
} | |
end, | |
[ "right" ] = function() | |
return { | |
x = ext.win.margin / 2 + screen.w / 2 + screen.x, | |
y = screen.y, | |
w = screen.w / 2 - ext.win.margin * (2 - 1 / 4), | |
h = screen.h / 2 - ext.win.margin * (2 - 1 / 4) | |
} | |
end | |
} | |
return frames[direction]() | |
end | |
-- returns frame moved by ext.win.margin | |
function ext.frame.nudge(frame, screen, direction) | |
local modifyframe = { | |
[ "up" ] = function(frame) | |
frame.y = math.max(screen.y + ext.win.margin, frame.y - ext.win.margin) | |
return frame | |
end, | |
[ "down" ] = function(frame) | |
frame.y = math.min(screen.y + screen.h - frame.h - ext.win.margin * 3 / 4, frame.y + ext.win.margin) | |
return frame | |
end, | |
[ "left" ] = function(frame) | |
frame.x = math.max(screen.x + ext.win.margin, frame.x - ext.win.margin) | |
return frame | |
end, | |
[ "right" ] = function(frame) | |
frame.x = math.min(screen.x + screen.w - frame.w - ext.win.margin, frame.x + ext.win.margin) | |
return frame | |
end | |
} | |
return modifyframe[direction](frame) | |
end | |
-- returns frame fited inside screen | |
function ext.frame.fit(screen, frame) | |
frame.w = math.min(frame.w, screen.w - ext.win.margin * 2) | |
frame.h = math.min(frame.h, screen.h - ext.win.margin * (2 - 1 / 4)) | |
return frame | |
end | |
-- returns frame centered inside screen | |
function ext.frame.center(screen, frame) | |
frame.x = screen.w / 2 - frame.w / 2 + screen.x | |
frame.y = screen.h / 2 - frame.h / 2 + screen.y | |
return frame | |
end | |
-- ugly fix for problem with window height when it's as big as screen | |
function ext.win.fix(win) | |
local screen = win:screen():frame_without_dock_or_menu() | |
local frame = win:frame() | |
if (frame.h > (screen.h - ext.win.margin * (2 - 1 / 4))) then | |
frame.h = screen.h - ext.win.margin * 10 | |
win:setframe(frame) | |
end | |
end | |
-- pushes window to next screen | |
function ext.win.pushwindow_nextscreen() | |
local win = window.focusedwindow() | |
ext.grid.set(win, ext.grid.get(win), win:screen():next()) | |
end | |
-- pushes window to previous screen | |
function ext.win.pushwindow_prevscreen() | |
local win = window.focusedwindow() | |
ext.grid.set(win, ext.grid.get(win), win:screen():previous()) | |
end | |
-- pushes window in direction and nudges to edge, fixes terminal positioning | |
function ext.win.push(win, direction) | |
local screen = win:screen():frame_without_dock_or_menu() | |
local frame | |
frame = ext.frame.push(screen, direction) | |
-- frame = ext.frame.nudge(frame, screen, direction) | |
ext.win.fix(win) | |
win:setframe(frame) | |
end | |
-- pushes window in direction and nudges to corner, fixes terminal positioning | |
function ext.win.corner(win, direction) | |
local screen = win:screen():frame_without_dock_or_menu() | |
local frame | |
frame = ext.frame.corner(screen, direction) | |
-- frame = ext.frame.nudge(frame, screen, direction) | |
ext.win.fix(win) | |
win:setframe(frame) | |
end | |
-- nudges window in direction | |
function ext.win.nudge(win, direction) | |
local screen = win:screen():frame_without_dock_or_menu() | |
local frame = win:frame() | |
frame = ext.frame.nudge(frame, screen, direction) | |
win:setframe(frame) | |
end | |
-- centers window | |
function ext.win.center(win) | |
local screen = win:screen():frame_without_dock_or_menu() | |
local frame = win:frame() | |
frame = ext.frame.center(screen, frame) | |
win:setframe(frame) | |
end | |
-- fullscreen window with ext.win.margin | |
function ext.win.full(win) | |
local screen = win:screen():frame_without_dock_or_menu() | |
local frame = { | |
x = ext.win.margin + screen.x, | |
y = screen.y, | |
w = screen.w - ext.win.margin * 2, | |
h = screen.h - ext.win.margin * (2 - 1 / 4) | |
} | |
ext.win.fix(win) | |
win:setframe(frame) | |
end | |
-- throw to next screen, center and fit | |
function ext.win.throw(win) | |
local screen = win:screen():next():frame_without_dock_or_menu() | |
local frame = win:frame() | |
frame.x = screen.x | |
frame.y = screen.y | |
frame = ext.frame.fit(screen, frame) | |
frame = ext.frame.center(screen, frame) | |
ext.win.fix(win) | |
win:setframe(frame) | |
win:focus() | |
end | |
-- set window size and center | |
function ext.win.size(win, size) | |
local screen = win:screen():frame_without_dock_or_menu() | |
local frame = win:frame() | |
frame.w = size.w | |
frame.h = size.h | |
frame = ext.frame.fit(screen, frame) | |
frame = ext.frame.center(screen, frame) | |
win:setframe(frame) | |
end | |
-- save and restore window positions | |
function ext.win.pos(win, option) | |
local id = win:application():bundleid() | |
local frame = win:frame() | |
-- saves window position if not saved before | |
if option == "save" and not ext.win.positions[id] then | |
ext.win.positions[id] = frame | |
end | |
-- force update saved window position | |
if option == "update" then | |
ext.win.positions[id] = frame | |
end | |
-- restores window position | |
if option == "load" and ext.win.positions[id] then | |
win:setframe(ext.win.positions[id]) | |
end | |
end | |
-- cycle application windows | |
-- simplified and stolen from: https://github.com/nifoc/dotfiles/blob/master/hydra/cycle.lua | |
function ext.win.cycle(win) | |
local windows = win:application():visiblewindows() | |
windows = fnutils.filter(windows, function(win) return win:isstandard() end) | |
if #windows >= 2 then | |
table.sort(windows, function(a, b) return a:id() < b:id() end) | |
local activewindowindex = fnutils.indexof(windows, win) | |
if activewindowindex then | |
activewindowindex = activewindowindex + 1 | |
if activewindowindex > #windows then activewindowindex = 1 end | |
windows[activewindowindex]:focus() | |
end | |
end | |
end | |
-- apply function to a window with optional params, saving it's position for restore | |
function dowin(fn, param) | |
return function() | |
local win = window.focusedwindow() | |
ext.win.pos(win, "save") | |
fn(win, param) | |
end | |
end | |
-- keyboard modifier for bindings | |
local mod = { "cmd", "ctrl"} | |
local mod1 = { "cmd", "ctrl", "alt" } | |
local mod2 = { "cmd", "alt" } | |
-- basic bindings | |
hotkey.bind(mod, "c", dowin(ext.win.center)) | |
hotkey.bind(mod, "m", dowin(ext.win.full)) | |
hotkey.bind(mod, "s", dowin(ext.win.pos, "update")) | |
hotkey.bind(mod, "r", dowin(ext.win.pos, "load")) | |
hotkey.bind(mod, "w", dowin(ext.win.cycle)) | |
hotkey.bind(mod, "tab", dowin(ext.win.throw)) | |
hotkey.bind(mod, "j", dowin(ext.win.pushwindow_prevscreen)) | |
hotkey.bind(mod, "k", dowin(ext.win.pushwindow_nextscreen)) | |
-- push to edges and nudge | |
fnutils.each({ "up", "down", "left", "right" }, function(direction) | |
hotkey.bind(mod, direction, dowin(ext.win.push, direction)) | |
hotkey.bind(mod1, direction, dowin(ext.win.corner, direction)) | |
hotkey.bind(mod2, direction, dowin(ext.win.nudge, direction)) | |
end) | |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment