forked from mogenson/PaperWM.spoon
-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathtiling.lua
More file actions
181 lines (161 loc) · 6.41 KB
/
tiling.lua
File metadata and controls
181 lines (161 loc) · 6.41 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
local Window <const> = hs.window
local Screen <const> = hs.screen
local Spaces <const> = hs.spaces
local Tiling = {}
Tiling._index = Tiling
---initialize module with reference to PaperWM
---@param paperwm PaperWM
function Tiling.init(paperwm)
Tiling.PaperWM = paperwm
end
---update the virtual x position for a table of windows on the specified space
---@param space Space
---@param windows Window[]
local function update_virtual_positions(space, windows, x)
local x_positions = Tiling.PaperWM.state.xPositions(space)
for _, window in ipairs(windows) do
x_positions[window:id()] = x
end
end
---tile a column of window by moving and resizing
---@param windows Window[] column of windows
---@param bounds Frame bounds to constrain column of tiled windows
---@param h number|nil set windows to specified height
---@param w number|nil set windows to specified width
---@param id number|nil id of window to set specific height
---@param h4id number|nil specific height for provided window id
---@return number width of tiled column
function Tiling.tileColumn(windows, bounds, h, w, id, h4id)
local last_window, frame
local bottom_gap = Tiling.PaperWM.windows.getGap("bottom")
for _, window in ipairs(windows) do
frame = window:frame()
w = w or frame.w -- take given width or width of first window
if bounds.x then -- set either left or right x coord
frame.x = bounds.x
elseif bounds.x2 then
frame.x = bounds.x2 - w
end
if h then -- set height if given
if id and h4id and window:id() == id then
frame.h = h4id -- use this height for window with id
else
frame.h = h -- use this height for all other windows
end
end
frame.y = bounds.y
frame.w = w
frame.y2 = math.min(frame.y2, bounds.y2) -- don't overflow bottom of bounds
Tiling.PaperWM.windows.moveWindow(window, frame)
bounds.y = math.min(frame.y2 + bottom_gap, bounds.y2)
last_window = window
end
-- expand last window height to bottom
if frame.y2 ~= bounds.y2 then
frame.y2 = bounds.y2
Tiling.PaperWM.windows.moveWindow(last_window, frame)
end
return w -- return width of column
end
---tile all column in a space by moving and resizing windows
---@param space Space
function Tiling.tileSpace(space)
if not space or Spaces.spaceType(space) ~= "user" then
Tiling.PaperWM.logger.e("current space invalid")
return
end
-- find screen for space
local screen = Screen(Spaces.spaceDisplay(space))
if not screen then
Tiling.PaperWM.logger.e("no screen for space")
return
end
-- if focused window is in space, tile from that
local anchor_window = (function()
local focused_window = Window.focusedWindow()
if focused_window and not Tiling.PaperWM.floating.isFloating(focused_window) and Spaces.windowSpaces(focused_window)[1] == space then
return focused_window
else
return Tiling.PaperWM.windows.getFirstVisibleWindow(space, screen:frame())
end
end)()
if not anchor_window then
Tiling.PaperWM.logger.e("no anchor window in space")
return
end
Tiling.PaperWM.logger.df("tiling from anchor window: %s (%d)", anchor_window:title(), anchor_window:id())
local anchor_index = Tiling.PaperWM.state.windowIndex(anchor_window)
if not anchor_index then
Tiling.PaperWM.logger.e("anchor index not found, refreshing windows")
Tiling.PaperWM.windows.refreshWindows() -- try refreshing the windows
return -- bail
end
-- get some global coordinates
local screen_frame <const> = screen:frame()
local left_margin <const> = screen_frame.x + Tiling.PaperWM.screen_margin
local right_margin <const> = screen_frame.x2 - Tiling.PaperWM.screen_margin
local canvas <const> = Tiling.PaperWM.windows.getCanvas(screen)
-- make sure anchor window is on screen
local anchor_frame = anchor_window:frame()
anchor_frame.x = math.max(anchor_frame.x, canvas.x)
anchor_frame.w = math.min(anchor_frame.w, canvas.w)
anchor_frame.h = math.min(anchor_frame.h, canvas.h)
if anchor_frame.x2 > canvas.x2 then
anchor_frame.x = canvas.x2 - anchor_frame.w
end
-- adjust anchor window column
local column = Tiling.PaperWM.state.windowList(space, anchor_index.col)
if not column then
Tiling.PaperWM.logger.e("no anchor window column")
return
end
-- TODO: need a minimum window height
if #column == 1 then
anchor_frame.y, anchor_frame.h = canvas.y, canvas.h
Tiling.PaperWM.windows.moveWindow(anchor_window, anchor_frame)
else
local n = #column - 1 -- number of other windows in column
local bottom_gap = Tiling.PaperWM.windows.getGap("bottom")
local h =
math.max(0, canvas.h - anchor_frame.h - (n * bottom_gap)) // n
local bounds = {
x = anchor_frame.x,
x2 = nil,
y = canvas.y,
y2 = canvas.y2,
}
Tiling.tileColumn(column, bounds, h, anchor_frame.w, anchor_window:id(), anchor_frame.h)
end
update_virtual_positions(space, column, anchor_frame.x)
local right_gap = Tiling.PaperWM.windows.getGap("right")
local left_gap = Tiling.PaperWM.windows.getGap("left")
-- tile windows from anchor right
local x = anchor_frame.x2 + right_gap
for col = anchor_index.col + 1, #(Tiling.PaperWM.state.windowList(space)) do
local bounds = {
x = math.min(x, right_margin),
x2 = nil,
y = canvas.y,
y2 = canvas.y2,
}
local column = Tiling.PaperWM.state.windowList(space, col)
local width = Tiling.tileColumn(column, bounds)
update_virtual_positions(space, column, x)
x = x + width + right_gap
end
-- tile windows from anchor left
local x2 = anchor_frame.x - left_gap
for col = anchor_index.col - 1, 1, -1 do
local bounds = {
x = nil,
x2 = math.max(x2, left_margin),
y = canvas.y,
y2 = canvas.y2,
}
local column = Tiling.PaperWM.state.windowList(space, col)
local width = Tiling.tileColumn(column, bounds)
update_virtual_positions(space, column, x2 - width)
x2 = x2 - width - left_gap
end
end
return Tiling