156 lines
4.6 KiB
Lua
156 lines
4.6 KiB
Lua
--- === hs.mjomatic ===
|
|
---
|
|
--- tmuxomatic-like window management
|
|
|
|
local mjomatic = {}
|
|
|
|
local alert = require 'hs.alert'
|
|
local application = require 'hs.application'
|
|
local screen = require 'hs.screen'
|
|
|
|
local gridh
|
|
local gridw
|
|
|
|
local function resizetogrid(window, coords)
|
|
-- alert.show(string.format('move window %q to %d,%d-%d,%d', window:title(), coords.r1, coords.c1, coords.r2, coords.c2), 20)
|
|
|
|
-- collect screen dimensions
|
|
local frame = screen.mainScreen():fullFrame()
|
|
local framew = screen.mainScreen():frame()
|
|
|
|
local h = framew.h
|
|
local w = frame.w
|
|
local x = framew.x
|
|
local y = framew.y
|
|
-- alert.show(string.format('screen dimensions %d,%d at %d,%d', h, w, x, y))
|
|
local hdelta = h / gridh
|
|
local wdelta = w / gridw
|
|
|
|
-- alert.show('hdelta='..hdelta, 5)
|
|
-- alert.show('wdelta='..wdelta, 5)
|
|
local newframe = {}
|
|
newframe.x = (coords.c1-1) * wdelta + x
|
|
newframe.y = (coords.r1-1) * hdelta + y
|
|
newframe.h = (coords.r2-coords.r1+1) * hdelta
|
|
newframe.w = (coords.c2-coords.c1+1) * wdelta
|
|
window:setFrame(newframe)
|
|
-- alert.show(string.format('new frame for %q is %d*%d at %d,%d', window:title(), newframe.w, newframe.h, newframe.x, newframe.y), 20)
|
|
end
|
|
|
|
--- hs.mjomatic.go(cfg)
|
|
--- Function
|
|
--- Applies a configuration to the currently open windows
|
|
---
|
|
--- Parameters:
|
|
--- * cfg - A table containing a series of strings, representing the desired window layout
|
|
---
|
|
--- Returns:
|
|
--- * None
|
|
---
|
|
--- Notes:
|
|
--- * An example use:
|
|
---
|
|
--- ~~~lua
|
|
--- mjomatic.go({
|
|
--- "CCCCCCCCCCCCCiiiiiiiiiii # <-- The windowgram, it defines the shapes and positions of windows",
|
|
--- "CCCCCCCCCCCCCiiiiiiiiiii",
|
|
--- "SSSSSSSSSSSSSiiiiiiiiiii",
|
|
--- "SSSSSSSSSSSSSYYYYYYYYYYY",
|
|
--- "SSSSSSSSSSSSSYYYYYYYYYYY",
|
|
--- "",
|
|
--- "C Google Chrome # <-- window C has application():title() 'Google Chrome'",
|
|
--- "i iTerm",
|
|
--- "Y YoruFukurou",
|
|
--- "S Sublime Text 2"})
|
|
--- ~~~
|
|
|
|
function mjomatic.go(cfg)
|
|
-- alert.show('mjomatic is go')
|
|
local grid = {}
|
|
local map = {}
|
|
|
|
local target = grid
|
|
|
|
|
|
-- FIXME move gsub stuff to separate function (iterator wrapper around io.lines?)
|
|
-- then do parsing in two loops so we don't need to muck about with target
|
|
-- and do some parsing inline
|
|
for _,l in ipairs(cfg) do
|
|
l = l:gsub('#.*','') -- strip comments
|
|
l = l:gsub('%s*$','') -- strip trailing whitespace
|
|
-- alert.show(l)
|
|
if l:len() == 0 then
|
|
if #grid > 0 then
|
|
if target == grid then
|
|
target = map
|
|
elseif #map > 0 then
|
|
error('config has more than two chunks')
|
|
end
|
|
end
|
|
else
|
|
table.insert(target, l)
|
|
end
|
|
end
|
|
|
|
-- alert.show('grid size '..#grid)
|
|
-- alert.show('map size '..#map)
|
|
|
|
gridh = #grid
|
|
gridw = nil
|
|
|
|
local windows = {}
|
|
local titlemap = {}
|
|
|
|
for _, v in ipairs(map) do
|
|
local key = v:sub(1,1)
|
|
local title = v:sub(3)
|
|
-- alert.show(string.format('%s=%s', key, title))
|
|
titlemap[title] = key
|
|
end
|
|
|
|
for row, v in ipairs(grid) do
|
|
if gridw then
|
|
if gridw ~= v:len() then
|
|
error('inconsistent grid width')
|
|
end
|
|
else
|
|
gridw=v:len()
|
|
end
|
|
|
|
for column = 1, #v do
|
|
local char = v:sub(column, column)
|
|
if not windows[char] then
|
|
-- new window, create it with size 1x1
|
|
windows[char] = {r1=row, c1=column, r2=row, c2=column}
|
|
else
|
|
-- expand it
|
|
windows[char].r2=row
|
|
windows[char].c2=column
|
|
end
|
|
end
|
|
end
|
|
|
|
-- alert.show('grid h='..gridh..' w='..gridw)
|
|
-- alert.show('windows:')
|
|
--for char, window in pairs(windows) do
|
|
-- alert.show(string.format('window %s: top left %d,%d bottom right %d,%d', char, window.r1, window.c1, window.r2, window.c2))
|
|
--end
|
|
|
|
for title, key in pairs(titlemap) do
|
|
-- alert.show(string.format("title %s key %s", title, key))
|
|
if not windows[key] then
|
|
error(string.format('no window found for application %s (%s)', title, key))
|
|
end
|
|
local app = application.get(title)
|
|
local window = app and app:mainWindow()
|
|
-- alert.show(string.format('application title for %q is %q, main window %q', title, app:title(), window:title()))
|
|
if window then
|
|
resizetogrid(window, windows[key])
|
|
else
|
|
alert.show(string.format('application %s has no main window', app:title()))
|
|
end
|
|
end
|
|
end
|
|
|
|
return mjomatic
|