Skip to content

Instantly share code, notes, and snippets.

@alankyshum
Last active October 23, 2022 05:42
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 alankyshum/43e33ecdc06c87dbc11a50ab22e62173 to your computer and use it in GitHub Desktop.
Save alankyshum/43e33ecdc06c87dbc11a50ab22e62173 to your computer and use it in GitHub Desktop.
hammerspoon - with window management

Prerequisite

# Install basic tools (only needed for the first time)
/bin/bash -c "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/HEAD/install.sh)"
brew install wget

# Install hammerspoon
brew install --cask hammerspoon
open /Applications/Hammerspoon.app
mv ~/.hammerspoon/init.lua ~/.hammerspoon/init.lua.bak
wget https://gist.github.com/alankyshum/43e33ecdc06c87dbc11a50ab22e62173/raw/b2428389da2de3e12d4303e6a7513412365aaf37/init.lua -O ~/.hammerspoon/init.lua

# Install Rectangle
brew install --cask rectangle

Configure Hammerspoon

Finally, open Hammerspoon preference from the Mac OS Menu Bar, and enable Accessibility permission

Step Screenshot
Configure hammerspoon image
Reload config from Mac Menu Bar image

Configure Rectangle

  1. Open the newly installed "Reactangle" app
Step Screenshot
Configure keyboard shortcut (Use any shortcuts you want) image
Launch on login
hyper = {"ctrl", "alt", "cmd"}
hs.window.animationDuration = 0.0
local sizes = {24/6, 24/9, 24/12, 24/15}
local fullScreenSizes = {1, 4/3, 2, 3}
local GRID = {w = 24, h = 24}
hs.grid.setGrid(GRID.w .. 'x' .. GRID.h)
hs.grid.MARGINX = 0
hs.grid.MARGINY = 0
local pressed = {
up = false,
down = false,
left = false,
right = false
}
function nextStep(dim, offs, cb)
if hs.window.focusedWindow() then
local axis = dim == 'w' and 'x' or 'y'
local oppDim = dim == 'w' and 'h' or 'w'
local oppAxis = dim == 'w' and 'y' or 'x'
local win = hs.window.frontmostWindow()
local id = win:id()
local screen = win:screen()
cell = hs.grid.get(win, screen)
local nextSize = sizes[1]
for i=1,#sizes do
if cell[dim] == GRID[dim] / sizes[i] and
(cell[axis] + (offs and cell[dim] or 0)) == (offs and GRID[dim] or 0)
then
nextSize = sizes[(i % #sizes) + 1]
break
end
end
cb(cell, nextSize)
if cell[oppAxis] ~= 0 and cell[oppAxis] + cell[oppDim] ~= GRID[oppDim] then
cell[oppDim] = GRID[oppDim]
cell[oppAxis] = 0
end
hs.grid.set(win, cell, screen)
end
end
function nextFullScreenStep()
if hs.window.focusedWindow() then
local win = hs.window.frontmostWindow()
local id = win:id()
local screen = win:screen()
cell = hs.grid.get(win, screen)
local nextSize = fullScreenSizes[1]
for i=1,#fullScreenSizes do
if cell.w == GRID.w / fullScreenSizes[i] and
cell.h == GRID.h and
-- cell.h == GRID.h / fullScreenSizes[i] and
cell.x == (GRID.w - GRID.w / fullScreenSizes[i]) / 2 and
cell.y == 0 then
-- cell.y == (GRID.h - GRID.h / fullScreenSizes[i]) / 2 then
nextSize = fullScreenSizes[(i % #fullScreenSizes) + 1]
break
end
end
cell.w = GRID.w / nextSize
-- cell.h = GRID.h / nextSize
cell.h = GRID.h -- always full height
cell.x = (GRID.w - GRID.w / nextSize) / 2
-- cell.y = (GRID.h - GRID.h / nextSize) / 2
cell.y = 0 -- always full height
hs.grid.set(win, cell, screen)
end
end
function fullDimension(dim)
if hs.window.focusedWindow() then
local win = hs.window.frontmostWindow()
local id = win:id()
local screen = win:screen()
cell = hs.grid.get(win, screen)
if (dim == 'x') then
cell = '0,0 ' .. GRID.w .. 'x' .. GRID.h
else
cell[dim] = GRID[dim]
cell[dim == 'w' and 'x' or 'y'] = 0
end
hs.grid.set(win, cell, screen)
end
end
hs.hotkey.bind(hyper, "down", function ()
pressed.down = true
if pressed.up then
fullDimension('h')
else
nextStep('h', true, function (cell, nextSize)
cell.y = GRID.h - GRID.h / nextSize
cell.h = GRID.h / nextSize
end)
end
end, function ()
pressed.down = false
end)
hs.hotkey.bind(hyper, "right", function ()
pressed.right = true
if pressed.left then
fullDimension('w')
else
nextStep('w', true, function (cell, nextSize)
cell.x = GRID.w - GRID.w / nextSize
cell.w = GRID.w / nextSize
end)
end
end, function ()
pressed.right = false
end)
hs.hotkey.bind(hyper, "left", function ()
pressed.left = true
if pressed.right then
fullDimension('w')
else
nextStep('w', false, function (cell, nextSize)
cell.x = 0
cell.w = GRID.w / nextSize
end)
end
end, function ()
pressed.left = false
end)
hs.hotkey.bind(hyper, "up", function ()
pressed.up = true
if pressed.down then
fullDimension('h')
else
nextStep('h', false, function (cell, nextSize)
cell.y = 0
cell.h = GRID.h / nextSize
end)
end
end, function ()
pressed.up = false
end)
hs.hotkey.bind(hyper, "f", function ()
nextFullScreenStep()
end)
hs.hotkey.bind(hyper, "i", function ()
local win = hs.window.frontmostWindow()
local id = win:id()
local screen = win:screen()
cell = hs.grid.get(win, screen)
hs.alert.show(cell)
end)
# Install basic tools (only needed for the first time)
/bin/bash -c "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/HEAD/install.sh)"
brew install wget
# Install hammerspoon
brew install --cask hammerspoon
mv ~/.hammerspoon/init.lua ~/.hammerspoon/init.lua.bak
wget https://gist.github.com/alankyshum/43e33ecdc06c87dbc11a50ab22e62173/raw/b2428389da2de3e12d4303e6a7513412365aaf37/init.lua -O ~/.hammerspoon/init.lua
# Install Rectangle
brew install --cask rectangle
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment