I’m starting to try using HammerSpoon, which is driven by Lua, for automatically arranging my applications depending on application scenarios.
I have 3 screens, including a MacBook Pro at Retina native resolution and a Seiki 4K display. I grabbed the names of the monitors from Display Menu‘s menu bar menu.
I initially made the mistake of looking for “Outlook”, “OneNote”, and “Chrome”, but all three applications require their respective company’s names to be included in the application name.
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
— https://en.wikipedia.org/wiki/Web_colors#X11_color_names | |
— hs.streamdeck.init(function(connected, sd) | |
— device = hs.streamdeck.getDevice(1) | |
— device.setButtonColor("1", hs.drawing.color.x11.teal) | |
— end) | |
applications = { "Messages", "iTunes", "Skype for Business", "iTerm2", "Microsoft Outlook", "HipChat", "Microsoft OneNote", "MacVim", "RubyMine", "Firefox" } | |
hs.hotkey.bind({"cmd", "alt", "ctrl"}, "pageup", function() | |
for _k, app_name in pairs(applications) do | |
hs.application.launchOrFocus(app_name) | |
print(app_name) | |
app = hs.application.find(app_name) | |
if app then | |
hs.application.unhide(app) | |
end | |
end | |
local lgUltra="LG Ultra HD" | |
local windowLayout = { | |
— upper right quadrant | |
{"Slack", nil, lgUltra, hs.geometry.rect(0,0,0.5,0.5), nil, nil}, | |
— upper left quadrant | |
{"Notes", nil, lgUltra, hs.geometry.rect(0.5,0,0.5,0.5), nil, nil}, | |
— lower right quadrant | |
{"Skype for Business", nil, lgUltra, hs.geometry.rect(0.5,0.5,0.5,0.5), nil, nil}, | |
— lower left quadrant | |
{"iTerm2", nil, lgUltra, hs.geometry.rect(0,0.5,0.5,0.5), nil, nil} | |
} | |
hs.layout.apply(windowLayout) | |
local laptopScreen="Color LCD" | |
local windowLayout = { | |
{"Microsoft Outlook", nil, laptopScreen, hs.geometry.rect(0.5,0,0.5,0.5), nil, nil}, | |
{"HipChat", nil, laptopScreen, hs.geometry.rect(0.5,0.5,0.5,0.5), nil, nil}, | |
{"Microsoft OneNote", nil, laptopScreen, hs.geometry.rect(0,0.5,0.5,0.5), nil, nil}, | |
{"Notes", nil, laptopScreen, hs.geometry.rect(0,0,0.5,0.5), nil, nil} | |
} | |
hs.layout.apply(windowLayout) | |
local seiki4KScreen="SE39UY04" | |
local windowLayout = { | |
{"MacVim", nil, seiki4KScreen, hs.geometry.rect(0,0,0.5,0.5), nil, nil}, | |
{"Emacs", nil, seiki4KScreen, hs.geometry.rect(0.5,0,0.5,0.5), nil, nil}, | |
{"RubyMine-EAP", nil, seiki4KScreen, hs.geometry.rect(0,0.5,0.5,0.5), nil, nil}, | |
{"iTerm2", nil, seiki4KScreen, hs.geometry.rect(0.5,0.5,0.5,0.5), nil, nil} | |
} | |
hs.layout.apply(windowLayout) | |
end) | |
— Convert a lua table into a lua syntactically correct string | |
function table_to_string(tbl) | |
local result = "{" | |
for k, v in pairs(tbl) do | |
— Check the key type (ignore any numerical keys – assume its an array) | |
if type(k) == "string" then | |
result = result.."[\""..k.."\"]".."=" | |
end | |
— Check the value type | |
if type(v) == "table" then | |
result = result..table_to_string(v) | |
elseif type(v) == "boolean" then | |
result = result..tostring(v) | |
else | |
result = result.."\""..v.."\"" | |
end | |
result = result.."," | |
end | |
— Remove leading commas from the result | |
if result ~= "" then | |
result = result:sub(1, result:len()–1) | |
end | |
return result.."}" | |
end | |
hs.hotkey.bind({}, "f13", function() | |
hs.location.start() | |
locationTable = hs.location.get() | |
hs.eventtap.keyStrokes(locationTable["latitude"] .. "," .. locationTable["longitude"]) | |
print(table_to_string(locationTable)) | |
end) | |
function move_to_third(r,c) | |
local win = hs.window.focusedWindow() | |
local f = win:frame() | |
local screen = win:screen() | |
local extents = screen:frame() | |
f.x = extents.x + (extents.w / 3) * c | |
f.y = extents.y + (extents.h / 3) * r | |
f.w = extents.w / 3 | |
f.h = extents.h / 3 | |
win:setFrame(f) | |
end | |
function move_to_two_third_horz(c) | |
local win = hs.window.focusedWindow() | |
local f = win:frame() | |
local screen = win:screen() | |
local extents = screen:frame() | |
f.x = extents.x + (extents.w / 3) * c | |
f.y = extents.y | |
f.w = extents.w / 3 * 2 | |
f.h = extents.h | |
win:setFrame(f) | |
end | |
function move_to_two_third_vert(r) | |
local win = hs.window.focusedWindow() | |
local f = win:frame() | |
local screen = win:screen() | |
local extents = screen:frame() | |
f.x = extents.x | |
f.y = extents.y + (extents.h / 3) * r | |
f.w = extents.w | |
f.h = extents.h / 3 * 2 | |
win:setFrame(f) | |
end | |
function move_to_four_ninths(r,c) | |
local win = hs.window.focusedWindow() | |
local f = win:frame() | |
local screen = win:screen() | |
local extents = screen:frame() | |
f.x = extents.x + (extents.w / 3) * c | |
f.y = extents.y + (extents.h / 3) * r | |
f.w = extents.w / 3 * 2 | |
f.h = extents.h / 3 * 2 | |
win:setFrame(f) | |
end | |
function move_to_fourth(r,c) | |
local win = hs.window.focusedWindow() | |
local f = win:frame() | |
local screen = win:screen() | |
local extents = screen:frame() | |
f.x = extents.x + (extents.w / 4) * c | |
f.y = extents.y + (extents.h / 4) * r | |
f.w = extents.w / 4 | |
f.h = extents.h / 4 | |
win:setFrame(f) | |
end | |
for i=0,15 do | |
hs.hotkey.bind({"cmd", "alt", "ctrl"}, string.format("%x", i), function() | |
move_to_fourth(math.floor(i/4),i%4) | |
end) | |
end | |
for i=1,9 do | |
hs.hotkey.bind({"cmd", "alt", "ctrl"}, "pad"..tostring(i), function() | |
move_to_third(math.floor((9–i)/3),(i–1)%3) | |
end) | |
end | |
for i=1,9 do | |
hs.hotkey.bind({"cmd", "alt", "ctrl", "shift"}, "pad"..tostring(i), function() | |
if i == 1 then move_to_four_ninths(1,0) | |
elseif i == 3 then move_to_four_ninths(1,1) | |
elseif i == 7 then move_to_four_ninths(0,0) | |
elseif i == 9 then move_to_four_ninths(0,1) | |
elseif i == 2 then move_to_two_third_vert(1) | |
elseif i == 4 then move_to_two_third_horz(0) | |
elseif i == 6 then move_to_two_third_horz(1) | |
elseif i == 8 then move_to_two_third_vert(0) | |
end | |
end) | |
end | |
— https://www.hammerspoon.org/docs/hs.application.watcher.html | |
— https://www.lua.org/pil/9.1.html | |
— https://stackoverflow.com/questions/16984540/how-to-pass-a-function-as-a-parameter-in-lua | |
— hs.application.watcher.new() | |
— TODO Plan: | |
— Create application watcher | |
— | |
currentApp = "" | |
function applicationWatcher(appName, eventType, appObject) | |
if(eventType == hs.application.watcher.activated) then | |
if (appName == "HipChat") then | |
currentApp = "HipChat" | |
end | |
if (appName == "Google Chrome") then | |
currentApp = "Chrome" | |
end | |
end | |
end | |
function alertCurrent() | |
hs.alert(currentApp) | |
end | |
—hs.timer.doEvery(5, alertCurrent) | |