mirror of
https://github.com/jimeh/dotfiles.git
synced 2026-02-19 04:46:41 +00:00
366 lines
9.3 KiB
Lua
366 lines
9.3 KiB
Lua
-- luacheck: read_globals hs
|
|
|
|
local eventtap = require('hs.eventtap')
|
|
local grid = require('hs.grid')
|
|
local hotkey = require('hs.hotkey')
|
|
local mouse = require('hs.mouse')
|
|
local timer = require('hs.timer')
|
|
local window = require('hs.window')
|
|
|
|
-- Configuration
|
|
local wm = {
|
|
animationDuration = 0.0,
|
|
gridSizes = { default = '30x20', interactive = '8x4' },
|
|
gridTextSize = 50,
|
|
margins = { w = 0, h = 0 }
|
|
}
|
|
|
|
-- Initialize and register keybindings
|
|
function wm:init()
|
|
-- setup
|
|
local bind = require('hs.hotkey').bind
|
|
local bindAndRepeat = self.bindAndRepeat
|
|
|
|
window.animationDuration = self.animationDuration
|
|
grid.setGrid(self.gridSizes.default)
|
|
grid.setMargins(self.margins)
|
|
grid.ui.textSize = self.gridTextSize
|
|
|
|
--
|
|
-- Move and resize to preset grid locations
|
|
--
|
|
|
|
-- Show interactive grid menu
|
|
bind({ 'cmd', 'ctrl' }, '§',
|
|
function()
|
|
grid.setGrid(self.gridSizes.interactive)
|
|
grid.show(
|
|
function()
|
|
grid.setGrid(self.gridSizes.default)
|
|
end
|
|
)
|
|
end
|
|
)
|
|
|
|
-- Left half
|
|
bind({ 'cmd', 'ctrl' }, 'J', self.adjustWindow(0, 0, 15, 20))
|
|
-- Right half
|
|
bind({ 'cmd', 'ctrl' }, 'L', self.adjustWindow(15, 0, 15, 20))
|
|
-- Top half
|
|
bind({ 'cmd', 'ctrl' }, 'I', self.adjustWindow(0, 0, 30, 10))
|
|
-- Bottom half
|
|
bind({ 'cmd', 'ctrl' }, 'K', self.adjustWindow(0, 10, 30, 10))
|
|
|
|
-- Left narrow
|
|
bind({ 'ctrl', 'alt' }, 'U', self.adjustWindow(0, 0, 12, 20))
|
|
-- Left super narrow
|
|
bind({ 'cmd', 'ctrl', 'alt' }, 'U', self.adjustWindow(0, 0, 9, 20))
|
|
-- Right narrow
|
|
bind({ 'ctrl', 'alt' }, 'O', self.adjustWindow(18, 0, 12, 20))
|
|
-- Right super narrow
|
|
bind({ 'cmd', 'ctrl', 'alt' }, 'O', self.adjustWindow(21, 0, 9, 20))
|
|
|
|
-- Left wide
|
|
bind({ 'cmd', 'ctrl' }, 'U', self.adjustWindow(0, 0, 18, 20))
|
|
-- Right wide
|
|
bind({ 'cmd', 'ctrl' }, 'O', self.adjustWindow(12, 0, 18, 20))
|
|
|
|
-- Center super narrow
|
|
bind({ 'cmd', 'ctrl', 'alt' }, '\\', self.adjustWindow(10, 0, 10, 20))
|
|
-- Center narrow small
|
|
bind({ 'ctrl', 'alt' }, '\\', self.adjustWindow(9, 0, 12, 20))
|
|
-- Center narrow
|
|
bind({ 'cmd', 'ctrl' }, '\\', self.adjustWindow(7, 0, 16, 20))
|
|
|
|
-- Center medium small
|
|
bind({ 'ctrl', 'alt' }, '\'', self.adjustWindow(6, 0, 18, 20))
|
|
-- Center medium
|
|
bind({ 'cmd', 'ctrl' }, '\'', self.adjustWindow(5, 0, 20, 20))
|
|
|
|
-- Center wide small
|
|
bind({ 'ctrl', 'alt' }, ';', self.adjustWindow(4, 0, 22, 20))
|
|
-- Center wide
|
|
bind({ 'cmd', 'ctrl' }, ';', self.adjustWindow(3, 0, 24, 20))
|
|
|
|
-- Maximized
|
|
bind({ 'cmd', 'ctrl' }, 'H', grid.maximizeWindow)
|
|
|
|
|
|
--
|
|
-- Move and resize windows
|
|
--
|
|
|
|
bind({ 'cmd', 'ctrl', 'alt' }, 'F', self.resizeWindow(900, 642))
|
|
bind({ 'cmd', 'ctrl', 'alt' }, 'X', self.adjustWindow(0, 3, 10, 14))
|
|
|
|
-- Resize windows
|
|
bindAndRepeat({ 'cmd', 'ctrl', 'alt' }, 'J', self.resizeWindowOnGrid(-1, 0))
|
|
bindAndRepeat({ 'cmd', 'ctrl', 'alt' }, 'L', self.resizeWindowOnGrid(1, 0))
|
|
bindAndRepeat({ 'cmd', 'ctrl', 'alt' }, 'I', self.resizeWindowOnGrid(0, -1))
|
|
bindAndRepeat({ 'cmd', 'ctrl', 'alt' }, 'K', self.resizeWindowOnGrid(0, 1))
|
|
|
|
-- Move window relative
|
|
bindAndRepeat({ 'ctrl', 'alt' }, 'J', self.moveWindowOnGrid(-1, 0))
|
|
bindAndRepeat({ 'ctrl', 'alt' }, 'L', self.moveWindowOnGrid(1, 0))
|
|
bindAndRepeat({ 'ctrl', 'alt' }, 'I', self.moveWindowOnGrid(0, -1))
|
|
bindAndRepeat({ 'ctrl', 'alt' }, 'K', self.moveWindowOnGrid(0, 1))
|
|
|
|
-- Enlarge horizontally
|
|
bindAndRepeat({ 'cmd', 'ctrl', 'shift' }, '\\',
|
|
self.resizeWindowOnGridSymmetrically(1, 0))
|
|
-- Shrink horizontally
|
|
bindAndRepeat({ 'cmd', 'ctrl', 'shift' }, '\'',
|
|
self.resizeWindowOnGridSymmetrically(-1, 0))
|
|
|
|
|
|
--
|
|
-- Move windows between spaces
|
|
--
|
|
|
|
bind({ 'ctrl', 'alt' }, 'left', self.moveWindowToSpace('left'))
|
|
bind({ 'ctrl', 'alt' }, 'right', self.moveWindowToSpace('right'))
|
|
bind({ 'ctrl', 'alt' }, '1', self.moveWindowToSpace('1'))
|
|
bind({ 'ctrl', 'alt' }, '2', self.moveWindowToSpace('2'))
|
|
bind({ 'ctrl', 'alt' }, '3', self.moveWindowToSpace('3'))
|
|
bind({ 'ctrl', 'alt' }, '4', self.moveWindowToSpace('4'))
|
|
bind({ 'ctrl', 'alt' }, '5', self.moveWindowToSpace('5'))
|
|
bind({ 'ctrl', 'alt' }, '6', self.moveWindowToSpace('6'))
|
|
bind({ 'ctrl', 'alt' }, '7', self.moveWindowToSpace('7'))
|
|
bind({ 'ctrl', 'alt' }, '8', self.moveWindowToSpace('8'))
|
|
bind({ 'ctrl', 'alt' }, '9', self.moveWindowToSpace('9'))
|
|
bind({ 'ctrl', 'alt' }, '0', self.moveWindowToSpace('0'))
|
|
|
|
|
|
--
|
|
-- Move windows between displays
|
|
--
|
|
|
|
-- Move to screen to the left
|
|
bind({ 'cmd', 'ctrl' }, ',',
|
|
function()
|
|
local win = window.focusedWindow()
|
|
win:moveOneScreenWest()
|
|
grid.snap(win)
|
|
end
|
|
)
|
|
|
|
-- Move to screen to the right
|
|
bind({ 'cmd', 'ctrl' }, '.',
|
|
function()
|
|
local win = window.focusedWindow()
|
|
win:moveOneScreenEast()
|
|
grid.snap(win)
|
|
end
|
|
)
|
|
|
|
-- Move to screen above
|
|
bind({ 'cmd', 'ctrl', 'alt' }, '.',
|
|
function()
|
|
local win = window.focusedWindow()
|
|
win:moveOneScreenNorth()
|
|
grid.snap(win)
|
|
end
|
|
)
|
|
|
|
-- Move to screen below
|
|
bind({ 'cmd', 'ctrl', 'alt' }, ',',
|
|
function()
|
|
local win = window.focusedWindow()
|
|
win:moveOneScreenSouth()
|
|
grid.snap(win)
|
|
end
|
|
)
|
|
end
|
|
|
|
--
|
|
-- Private methods
|
|
--
|
|
|
|
wm.bindAndRepeat = function(mod, key, fn)
|
|
hotkey.bind(mod, key, fn, nil, fn)
|
|
end
|
|
|
|
wm.adjustWindow = function(x, y, w, h)
|
|
return function()
|
|
grid.adjustWindow(
|
|
function(cell)
|
|
cell.x = x
|
|
cell.y = y
|
|
cell.w = w
|
|
cell.h = h
|
|
end
|
|
)
|
|
end
|
|
end
|
|
|
|
wm.resizeWindow = function(w, h)
|
|
return function()
|
|
local win = window.focusedWindow()
|
|
local f = win:frame()
|
|
|
|
f.w = w
|
|
f.h = h
|
|
win:setFrame(f)
|
|
end
|
|
end
|
|
|
|
wm.moveWindow = function(x, y)
|
|
return function()
|
|
local win = window.focusedWindow()
|
|
local f = win:frame()
|
|
|
|
f.x = x
|
|
f.y = y
|
|
win:setFrame(f)
|
|
end
|
|
end
|
|
|
|
wm.moveWindowRelative = function(x, y)
|
|
return function()
|
|
local win = window.focusedWindow()
|
|
local f = win:frame()
|
|
|
|
f.x = f.x + x
|
|
f.y = f.y + y
|
|
win:setFrame(f)
|
|
end
|
|
end
|
|
|
|
wm.moveWindowOnGrid = function(x, y)
|
|
return function()
|
|
grid.adjustWindow(
|
|
function(cell)
|
|
local max = grid.getGrid()
|
|
|
|
if ((cell.x + x) + cell.w) <= max.w then
|
|
cell.x = cell.x + x
|
|
end
|
|
|
|
if ((cell.y + y) + cell.h) <= max.h then
|
|
cell.y = cell.y + y
|
|
end
|
|
end
|
|
)
|
|
end
|
|
end
|
|
|
|
wm.resizeWindowOnGrid = function(w, h)
|
|
return function()
|
|
grid.adjustWindow(
|
|
function(cell)
|
|
local max = grid.getGrid()
|
|
|
|
if cell.x == 0 and cell.w == max.w then
|
|
if w < 0 then
|
|
cell.w = cell.w + w
|
|
else
|
|
cell.w = cell.w - w
|
|
cell.x = cell.x + w
|
|
end
|
|
elseif (cell.x + cell.w) >= max.w then
|
|
cell.w = cell.w - w
|
|
cell.x = cell.x + w
|
|
elseif cell.x == 0 then
|
|
cell.w = cell.w + w
|
|
else
|
|
cell.w = cell.w + (w * 2)
|
|
cell.x = cell.x - w
|
|
end
|
|
|
|
if cell.y == 0 and cell.h == max.h then
|
|
if h < 0 then
|
|
cell.h = cell.h + h
|
|
else
|
|
cell.h = cell.h - h
|
|
cell.y = cell.y + h
|
|
end
|
|
elseif (cell.y + cell.h) >= max.h then
|
|
cell.h = cell.h - h
|
|
cell.y = cell.y + h
|
|
elseif cell.y == 0 then
|
|
cell.h = cell.h + h
|
|
else
|
|
cell.h = cell.h - (h * 2)
|
|
cell.y = cell.y + h
|
|
end
|
|
end
|
|
)
|
|
end
|
|
end
|
|
|
|
wm.resizeWindowOnGridSymmetrically = function(w, h)
|
|
return function()
|
|
grid.adjustWindow(
|
|
function(cell)
|
|
local max = grid.getGrid()
|
|
|
|
if w ~= 0 and cell.w + (w * 2) >= 2 then
|
|
if (cell.w + (w * 2)) <= max.w then
|
|
cell.w = cell.w + (w * 2)
|
|
cell.x = cell.x - w
|
|
elseif cell.w + w == max.w then
|
|
cell.w = max.w
|
|
cell.x = 0
|
|
end
|
|
end
|
|
|
|
if h ~= 0 and cell.h + (h * 2) >= 2 then
|
|
if (cell.h + (h * 2)) <= max.h then
|
|
cell.h = cell.h + (h * 2)
|
|
cell.y = cell.y - h
|
|
elseif cell.h + h == max.h then
|
|
cell.h = max.h
|
|
cell.y = 0
|
|
end
|
|
end
|
|
end
|
|
)
|
|
end
|
|
end
|
|
|
|
-- moveWindowToSpace
|
|
---
|
|
-- Requires ctrl+<left>/<right> and ctrl+<num> system keybindings, originally
|
|
-- from:
|
|
-- https://github.com/Hammerspoon/hammerspoon/issues/235#issuecomment-101069303
|
|
wm.moveWindowToSpace = function(direction)
|
|
return function()
|
|
local mouseOrigin = mouse.absolutePosition()
|
|
local win = window.focusedWindow()
|
|
local clickPoint = win:zoomButtonRect()
|
|
|
|
-- click and hold next to the zoom button close to the top of the window
|
|
clickPoint.x = clickPoint.x + clickPoint.w + 2
|
|
clickPoint.y = win:frame().y + 7
|
|
|
|
local mouseClickEvent = eventtap.event.newMouseEvent(
|
|
eventtap.event.types.leftMouseDown, clickPoint
|
|
)
|
|
mouseClickEvent:post()
|
|
timer.usleep(150000)
|
|
|
|
local nextSpaceDownEvent = eventtap.event.newKeyEvent(
|
|
{ "ctrl" }, direction, true
|
|
)
|
|
nextSpaceDownEvent:post()
|
|
timer.usleep(150000)
|
|
|
|
local nextSpaceUpEvent = eventtap.event.newKeyEvent(
|
|
{ "ctrl" }, direction, false
|
|
)
|
|
nextSpaceUpEvent:post()
|
|
timer.usleep(150000)
|
|
|
|
local mouseReleaseEvent = eventtap.event.newMouseEvent(
|
|
eventtap.event.types.leftMouseUp, clickPoint
|
|
)
|
|
mouseReleaseEvent:post()
|
|
timer.usleep(150000)
|
|
|
|
mouse.absolutePosition(mouseOrigin)
|
|
end
|
|
end
|
|
|
|
-- the end
|
|
return wm
|