Repository navigation
Expand file tree
/
Copy pathWindowPlacer.lua
More file actions
212 lines (171 loc) · 6.4 KB
/
Copy pathWindowPlacer.lua
File metadata and controls
212 lines (171 loc) · 6.4 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
---
--- Generated by Luanalysis
--- Created by muescha.
--- DateTime: 26.01.23 02:12
---
--- done with ChatGPT :)
--- can you write me an hammerspoon lua script where i can place a window on
--- main screen on macbook and the other one on halfscreen (top and bottom)
--- of my portrait mode external monitor.
---
--- can it done more generic for the external monitor, so that it find out by
--- self if a monitor external, and if it is in portrait mode it should place
--- on top or bottom and if it is in landscape mode it should be placed on left and right
fileInfo()
local units = require("hs.geometry").rect
debugTable(hs.screen.allScreens())
for k, v in pairs(hs.screen.allScreens()) do
print(v:currentMode().desc .. ": " .. v:name())
end
local function to_unit(values, rotate)
if rotate then
return units(values.start, 0, values.size, 1)
else
return units(0, values.start, 1, values.size)
end
end
-- TODO make this as setup
local layout_setup = {
-- key, start, size, info
{ key = "4", start = 0, size = 1, info = "+========X========+" }, -- max
{ key = "1", start = 0, size = 1/3, info = "+==X==+-----+-----+" },
{ key = "2", start = 1/3, size = 1/3, info = "+-----+==X==+-----+" },
{ key = "3", start = 2/3, size = 1/3, info = "+-----+-----+==X==+" },
{ key = "5", start = 0, size = 2/3, info = "+=====X=====+-----+" },
{ key = "6", start = 2/6, size = 3/6, info = "+-----+===X====+--+" },
{ key = "7", start = 1/3, size = 2/3, info = "+-----+=====X=====+" }, -- udemy
{ key = "8", start = 1/2, size = 1/2, info = "+--------+====X===+" }, -- bottom
{ key = "a", start = 0, size = 1/2, info = "+=====X==+-----+--+" }, -- top
{ key = "b", start = 1/2, size = 2/6, info = "+--------+==X==+--+" },
{ key = "c", start = 5/6, size = 1/6, info = "+--------+-----+X=+" },
{ key = "d", start = 0, size = 5/6, info = "+=======X======+--+" },
{ key = "e", start = 6/12, size = 3/12, info = "+--------+=X=+-+--+" },
{ key = "f", start = 9/12, size = 1/12, info = "+--------+---+X+--+" },
}
local layout_keys = {
maximize = "4",
top = "a",
bottom = "8",
udemy = "7",
}
-- TODO make this as config
local screens = {
internal = "Built%-in",
monitor_1 = "Thunderbolt",
monitor_2 = "5120x1440",
}
local layout = {
portrait = {},
landscape = {},
}
local function formatLayoutInfo(info, key, symbols)
return "key " .. key .. ": " .. info
:gsub("%+([=X])", symbols.left .. "%1") -- ←→ ◄ ► ◂▸ ‹› ╞╡
:gsub("([=X])%+", "%1".. symbols.right)
:gsub("X", key)
:gsub("%+", symbols.middle) -- ·¦│
:gsub("%-", " ")
:gsub("=", "─")
end
local function getSymbols(text)
local symbol_sets = {
{ left = "╞", right = "╡", middle = " " },
{ left = "╞", right = "╡", middle = "·" },
{ left = "╞", right = "╡", middle = "│" },
{ left = "←", right = "→", middle = " " },
{ left = "‹", right = "›", middle = " " },
}
math.randomseed(os.time())
local layout_number = math.random(#symbol_sets)
local symbols = symbol_sets[layout_number]
return { layout = layout_number, set = symbols }
end
local function createLayoutInfo(layout_setup)
local symbols = getSymbols()
local layout_info = "Layout (" .. symbols.layout .."): "
for _, v in ipairs(layout_setup) do
layout_info = layout_info .. "\n" .. formatLayoutInfo(v.info, v.key, symbols.set)
end
return layout_info
end
for _, v in ipairs(layout_setup) do
layout.portrait[v.key] = to_unit(v, false)
layout.landscape[v.key] = to_unit(v, true)
end
local function framesEqual(f1, f2, tolerance)
tolerance = tolerance or 1
return math.abs(f1.x - f2.x) < tolerance
and math.abs(f1.y - f2.y) < tolerance
and math.abs(f1.w - f2.w) < tolerance
and math.abs(f1.h - f2.h) < tolerance
end
local function windowMoveToUnit(window, screen, newUnit)
if window:screen() ~= screen then
return
end
local expectedFrame = screen:fromUnitRect(newUnit)
local actualFrame = window:frame()
if framesEqual(actualFrame, expectedFrame) then
debugInfo("No Adjustment needed...")
return false
end
debugInfo("Adjustment needed...")
window:moveToUnit(newUnit)
return true
end
local function windowMoveToKey(screenId, key)
local screen = hs.screen.find(screenId)
if screen == nil then
hs.alert.show("No screen available for keyword: " .. screenId)
return
end
local window = hs.window.focusedWindow()
local direction = screen:rotate() == 0 and "landscape" or "portrait"
local newUnit = layout[direction][key]
window:moveToScreen(screen)
-- retryWhile(function() return windowMoveToUnit(window, screen, newUnit) end)
retryWhileOnComplete(
function()
return windowMoveToUnit(window, screen, newUnit)
end,
function(success)
print(success and "Success: placed the window on right place!" or "Failed to place windows!")
end
)
end
local function startFn()
local window = hs.window.frontmostWindow()
local layout_info = createLayoutInfo(layout_setup)
debugInfo(layout_info)
hs.alert.show(layout_info, { textFont = "Menlo" }, window, "do-not-close")
end
local function exitFn()
hs.alert.closeAll()
end
local hkbm = hotkeybindmodal(hyper, "w", keyInfo("place on Big Monitor "), startFn, exitFn)
for _, v in ipairs(layout_setup) do
local key = v.key
local move = function()
windowMoveToKey(screens.monitor_2, key)
hkbm:exit()
end
hkbm:bind({}, key, move)
hkbm:bind(hyper, key, move)
end
local opts_keys = {
SCREEN = 1,
LAYOUT = 2,
DESC = 3,
}
local layout_non_modal = {
["1"] = { screens.monitor_1, layout_keys.top, "place on one half of monitor" },
["2"] = { screens.monitor_1, layout_keys.bottom, "place on other half of monitor" },
["3"] = { screens.internal, layout_keys.maximize, "place on main screen" },
["4"] = { screens.monitor_1, layout_keys.maximize, "place fullscreen on monitor" },
["8"] = { screens.monitor_1, layout_keys.udemy, "place 2/3 on monitor (Udemy Mode)" },
}
for key, opts in pairs(layout_non_modal) do
hs.hotkey.bind(hyper, key, keyInfo(opts[opts_keys.DESC]), function()
windowMoveToKey(opts[opts_keys.SCREEN], opts[opts_keys.LAYOUT])
end)
end