termfair.lua 9.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283
  1. --[[
  2. Licensed under GNU General Public License v2
  3. * (c) 2014, projektile
  4. * (c) 2013, Luca CPZ
  5. * (c) 2010, Nicolas Estibals
  6. * (c) 2010-2012, Peter Hofmann
  7. --]]
  8. local math = math
  9. local screen = screen
  10. local tonumber = tonumber
  11. local termfair = { name = "termfair" }
  12. termfair.center = { name = "centerfair" }
  13. termfair.stable = { name = "stablefair" }
  14. local function do_fair(p, orientation)
  15. local t = p.tag or screen[p.screen].selected_tag
  16. local wa = p.workarea
  17. local cls = p.clients
  18. if #cls == 0 then return end
  19. -- How many vertical columns? Read from nmaster on the tag.
  20. local num_x = tonumber(termfair.nmaster) or t.master_count
  21. local ncol = tonumber(termfair.ncol) or t.column_count
  22. if num_x <= 2 then num_x = 2 end
  23. if ncol <= 1 then ncol = 1 end
  24. local width = math.floor(wa.width/num_x)
  25. if orientation == "west" then
  26. -- Layout with fixed number of vertical columns (read from nmaster).
  27. -- New windows align from left to right. When a row is full, a new
  28. -- one above it is created. Like this:
  29. -- (1) (2) (3)
  30. -- +---+---+---+ +---+---+---+ +---+---+---+
  31. -- | | | | | | | | | | | |
  32. -- | 1 | | | -> | 1 | 2 | | -> | 1 | 2 | 3 | ->
  33. -- | | | | | | | | | | | |
  34. -- +---+---+---+ +---+---+---+ +---+---+---+
  35. -- (4) (5) (6)
  36. -- +---+---+---+ +---+---+---+ +---+---+---+
  37. -- | 1 | | | | 1 | 2 | | | 1 | 2 | 3 |
  38. -- +---+---+---+ -> +---+---+---+ -> +---+---+---+
  39. -- | 2 | 3 | 4 | | 3 | 4 | 5 | | 4 | 5 | 6 |
  40. -- +---+---+---+ +---+---+---+ +---+---+---+
  41. local num_y = math.max(math.ceil(#cls / num_x), ncol)
  42. local height = math.floor(wa.height/num_y)
  43. local cur_num_x = num_x
  44. local at_x = 0
  45. local at_y = 0
  46. local remaining_clients = #cls
  47. -- We start the first row. Left-align by limiting the number of
  48. -- available slots.
  49. if remaining_clients < num_x then
  50. cur_num_x = remaining_clients
  51. end
  52. -- Iterate in reversed order.
  53. for i = #cls,1,-1 do
  54. -- Get x and y position.
  55. local c = cls[i]
  56. local this_x = cur_num_x - at_x - 1
  57. local this_y = num_y - at_y - 1
  58. -- Calculate geometry.
  59. local g = {}
  60. if this_x == (num_x - 1) then
  61. g.width = wa.width - (num_x - 1)*width
  62. else
  63. g.width = width
  64. end
  65. if this_y == (num_y - 1) then
  66. g.height = wa.height - (num_y - 1)*height
  67. else
  68. g.height = height
  69. end
  70. g.x = wa.x + this_x*width
  71. g.y = wa.y + this_y*height
  72. if g.width < 1 then g.width = 1 end
  73. if g.height < 1 then g.height = 1 end
  74. p.geometries[c] = g
  75. remaining_clients = remaining_clients - 1
  76. -- Next grid position.
  77. at_x = at_x + 1
  78. if at_x == num_x then
  79. -- Row full, create a new one above it.
  80. at_x = 0
  81. at_y = at_y + 1
  82. -- We start a new row. Left-align.
  83. if remaining_clients < num_x then
  84. cur_num_x = remaining_clients
  85. end
  86. end
  87. end
  88. elseif orientation == "stable" then
  89. -- Layout with fixed number of vertical columns (read from nmaster).
  90. -- New windows align from left to right. When a row is full, a new
  91. -- one below it is created. Like this:
  92. -- (1) (2) (3)
  93. -- +---+---+---+ +---+---+---+ +---+---+---+
  94. -- | | | | | | | | | | | |
  95. -- | 1 | | | -> | 1 | 2 | | -> | 1 | 2 | 3 | ->
  96. -- | | | | | | | | | | | |
  97. -- +---+---+---+ +---+---+---+ +---+---+---+
  98. -- (4) (5) (6)
  99. -- +---+---+---+ +---+---+---+ +---+---+---+
  100. -- | 1 | 2 | 3 | | 1 | 2 | 3 | | 1 | 2 | 3 |
  101. -- +---+---+---+ +---+---+---+ +---+---+---+
  102. -- | 4 | | | | 4 | 5 | | | 4 | 5 | 6 |
  103. -- +---+---+---+ -> +---+---+---+ -> +---+---+---+
  104. local num_y = math.max(math.ceil(#cls / num_x), ncol)
  105. local height = math.floor(wa.height/num_y)
  106. for i = #cls,1,-1 do
  107. -- Get x and y position.
  108. local c = cls[i]
  109. local this_x = (i - 1) % num_x
  110. local this_y = math.floor((i - this_x - 1) / num_x)
  111. -- Calculate geometry.
  112. local g = {}
  113. if this_x == (num_x - 1) then
  114. g.width = wa.width - (num_x - 1)*width
  115. else
  116. g.width = width
  117. end
  118. if this_y == (num_y - 1) then
  119. g.height = wa.height - (num_y - 1)*height
  120. else
  121. g.height = height
  122. end
  123. g.x = wa.x + this_x*width
  124. g.y = wa.y + this_y*height
  125. if g.width < 1 then g.width = 1 end
  126. if g.height < 1 then g.height = 1 end
  127. p.geometries[c] = g
  128. end
  129. elseif orientation == "center" then
  130. -- Layout with fixed number of vertical columns (read from nmaster).
  131. -- Cols are centerded until there is nmaster columns, then windows
  132. -- are stacked in the slave columns, with at most ncol clients per
  133. -- column if possible.
  134. -- with nmaster=3 and ncol=1 you'll have
  135. -- (1) (2) (3)
  136. -- +---+---+---+ +-+---+---+-+ +---+---+---+
  137. -- | | | | | | | | | | | | |
  138. -- | | 1 | | -> | | 1 | 2 | | -> | 1 | 2 | 3 | ->
  139. -- | | | | | | | | | | | | |
  140. -- +---+---+---+ +-+---+---+-+ +---+---+---+
  141. -- (4) (5)
  142. -- +---+---+---+ +---+---+---+
  143. -- | | | 3 | | | 2 | 4 |
  144. -- + 1 + 2 +---+ -> + 1 +---+---+
  145. -- | | | 4 | | | 3 | 5 |
  146. -- +---+---+---+ +---+---+---+
  147. if #cls < num_x then
  148. -- Less clients than the number of columns, let's center it!
  149. local offset_x = wa.x + (wa.width - #cls*width) / 2
  150. for i = 1, #cls do
  151. local g = { y = wa.y }
  152. g.width = width
  153. g.height = wa.height
  154. if g.width < 1 then g.width = 1 end
  155. if g.height < 1 then g.height = 1 end
  156. g.x = offset_x + (i - 1) * width
  157. p.geometries[cls[i]] = g
  158. end
  159. else
  160. -- More clients than the number of columns, let's arrange it!
  161. -- Master client deserves a special treatement
  162. local g = {}
  163. g.width = wa.width - (num_x - 1)*width
  164. g.height = wa.height
  165. if g.width < 1 then g.width = 1 end
  166. if g.height < 1 then g.height = 1 end
  167. g.x = wa.x
  168. g.y = wa.y
  169. p.geometries[cls[1]] = g
  170. -- Treat the other clients
  171. -- Compute distribution of clients among columns
  172. local num_y = {}
  173. local remaining_clients = #cls-1
  174. local ncol_min = math.ceil(remaining_clients/(num_x-1))
  175. if ncol >= ncol_min then
  176. for i = (num_x-1), 1, -1 do
  177. if (remaining_clients-i+1) < ncol then
  178. num_y[i] = remaining_clients-i + 1
  179. else
  180. num_y[i] = ncol
  181. end
  182. remaining_clients = remaining_clients - num_y[i]
  183. end
  184. else
  185. local rem = remaining_clients % (num_x-1)
  186. if rem == 0 then
  187. for i = 1, num_x-1 do
  188. num_y[i] = ncol_min
  189. end
  190. else
  191. for i = 1, num_x-1 do
  192. num_y[i] = ncol_min - 1
  193. end
  194. for i = 0, rem-1 do
  195. num_y[num_x-1-i] = num_y[num_x-1-i] + 1
  196. end
  197. end
  198. end
  199. -- Compute geometry of the other clients
  200. local nclient = 2 -- we start with the 2nd client
  201. local wx = g.x + g.width
  202. for i = 1, (num_x-1) do
  203. local height = math.floor(wa.height / num_y[i])
  204. local wy = wa.y
  205. for _ = 0, (num_y[i]-2) do
  206. g = {}
  207. g.x = wx
  208. g.y = wy
  209. g.height = height
  210. g.width = width
  211. if g.width < 1 then g.width = 1 end
  212. if g.height < 1 then g.height = 1 end
  213. p.geometries[cls[nclient]] = g
  214. nclient = nclient + 1
  215. wy = wy + height
  216. end
  217. g = {}
  218. g.x = wx
  219. g.y = wy
  220. g.height = wa.height - (num_y[i] - 1)*height
  221. g.width = width
  222. if g.width < 1 then g.width = 1 end
  223. if g.height < 1 then g.height = 1 end
  224. p.geometries[cls[nclient]] = g
  225. nclient = nclient + 1
  226. wx = wx + width
  227. end
  228. end
  229. end
  230. end
  231. function termfair.center.arrange(p)
  232. return do_fair(p, "center")
  233. end
  234. function termfair.stable.arrange(p)
  235. return do_fair(p, "stable")
  236. end
  237. function termfair.arrange(p)
  238. return do_fair(p, "west")
  239. end
  240. return termfair