1. ---------------------------------------------------------------------------
  2. -- @author Donald Ephraim Curtis <dcurtis@cs.uiowa.edu>
  3. -- @author Julien Danjou <julien@danjou.info>
  4. -- @copyright 2009 Donald Ephraim Curtis
  5. -- @copyright 2008 Julien Danjou
  6. -- @release v3.5
  7. ---------------------------------------------------------------------------
  8.  
  9.  
  10. local padding = 24
  11. local gap_multiplier = 1
  12.  
  13. -- Grab environment we need
  14. local ipairs = ipairs
  15. local math = math
  16. local tag = require("awful.tag")
  17.  
  18. --- Tiled layouts module for awful
  19. -- awful.layout.suit.tile
  20. local tile = {}
  21.  
  22. local function tile_group(cls, wa, orientation, fact, group)
  23.     -- get our orientation right
  24.     local height = "height"
  25.     local width = "width"
  26.     local x = "x"
  27.     local y = "y"
  28.     if orientation == "top" or orientation == "bottom" then
  29.         height = "width"
  30.         width = "height"
  31.         x = "y"
  32.         y = "x"
  33.     end
  34.  
  35.     -- make this more generic (not just width)
  36.     local available = wa[width] - (group.coord - wa[x])
  37.  
  38.     -- find our total values
  39.     local total_fact = 0
  40.     local min_fact = 1
  41.     local size = group.size
  42.     for c = group.first,group.last do
  43.         -- determine the width/height based on the size_hint
  44.         local i = c - group.first +1
  45.         local size_hints = cls[c].size_hints
  46.         local size_hint = size_hints["min_"..width] or size_hints["base_"..width] or 0
  47.         size_hint = size_hint + cls[c].border_width*2
  48.         size = math.max(size_hint, size)
  49.  
  50.         -- calculate the height
  51.         if not fact[i] then
  52.             fact[i] = min_fact
  53.         else
  54.             min_fact = math.min(fact[i],min_fact)
  55.         end
  56.         total_fact = total_fact + fact[i]
  57.     end
  58.     size = math.min(size, available)
  59.  
  60.     local coord = wa[y]
  61.     local geom = {}
  62.     local used_size = 0
  63.     local unused = wa[height]
  64.     for c = group.first,group.last do
  65.         local i = c - group.first + 1
  66.         geom[width] = size - cls[c].border_width * 2 - (2 * padding)
  67.         geom[height] = math.floor(unused * fact[i] / total_fact) - cls[c].border_width * 2 - (padding * 2)
  68.         geom[x] = group.coord + padding
  69.         geom[y] = coord + padding
  70.         geom = cls[c]:geometry(geom)
  71.  
  72.         coord = coord + geom[height] + cls[c].border_width * 2 + padding
  73.         unused = unused - geom[height] - cls[c].border_width * 2 - padding
  74.         total_fact = total_fact - fact[i]
  75.         used_size = math.max(used_size, geom[width]) + cls[c].border_width * 2
  76.     end
  77.  
  78.     return used_size + (gap_multiplier * padding)
  79. end
  80.  
  81. local function do_tile(param, orientation)
  82.  
  83.     local t = tag.selected(param.screen)
  84.     orientation = orientation or "right"
  85.  
  86.     -- this handles are different orientations
  87.     local height = "height"
  88.     local width = "width"
  89.     local x = "x"
  90.     local y = "y"
  91.     if orientation == "top" or orientation == "bottom" then
  92.         height = "width"
  93.         width = "height"
  94.         x = "y"
  95.         y = "x"
  96.     end
  97.  
  98.     local cls = param.clients
  99.     local nmaster = math.min(tag.getnmaster(t), #cls)
  100.     local nother = math.max(#cls - nmaster,0)
  101.  
  102.     local mwfact = tag.getmwfact(t)
  103.     local wa = param.workarea
  104.     local ncol = tag.getncol(t)
  105.  
  106.     local data = tag.getdata(t).windowfact
  107.  
  108.     if not data then
  109.         data = {}
  110.         tag.getdata(t).windowfact = data
  111.     end
  112.  
  113.     local coord = wa[x]
  114.     local place_master = true
  115.     if orientation == "left" or orientation == "top" then
  116.         -- if we are on the left or top we need to render the other windows first
  117.         place_master = false
  118.     end
  119.  
  120.     -- this was easier than writing functions because there is a lot of data we need
  121.     for d = 1,2 do
  122.         if place_master and nmaster > 0 then
  123.             local size = wa[width]
  124.             if not data[0] then
  125.                 data[0] = {}
  126.             end
  127.             coord = coord + tile_group(cls, wa, orientation, data[0], {first=1, last=nmaster, coord = coord, size = size})
  128.         end
  129.  
  130.         if not place_master and nother > 0 then
  131.             --first = first + 1
  132.             local last = nmaster
  133.  
  134.             -- we have to modify the work area size to consider left and top views
  135.             local wasize = wa[width]
  136.             if nmaster > 0 and (orientation == "left" or orientation == "top") then
  137.                 wasize = wa[width] - wa[width] * mwfact
  138.             end
  139.             for i = 1,ncol do
  140.                 -- Try to get equal width among remaining columns
  141.                 local size = math.min( (wasize - (coord - wa[x])) / (ncol - i + 1) )
  142.                 local first = last + 1
  143.                 last = last + math.floor((#cls - last)/(ncol - i + 1))
  144.                 -- tile the column and update our current x coordinate
  145.                 if not data[i] then
  146.                     data[i] = {}
  147.                 end
  148.                
  149.                 coord = coord + tile_group(cls, wa, orientation, data[i], { first = first, last = last, coord = coord, size = size })
  150.             end
  151.         end
  152.         place_master = not place_master
  153.     end
  154.  
  155. end
  156.  
  157. tile.right = {}
  158. tile.right.name = "tile"
  159. tile.right.arrange = do_tile
  160.  
  161. --- The main tile algo, on left.
  162. -- @param screen The screen number to tile.
  163. tile.left = {}
  164. tile.left.name = "tileleft"
  165. function tile.left.arrange(p)
  166.     return do_tile(p, "left")
  167. end
  168.  
  169. --- The main tile algo, on bottom.
  170. -- @param screen The screen number to tile.
  171. tile.bottom = {}
  172. tile.bottom.name = "tilebottom"
  173. function tile.bottom.arrange(p)
  174.     return do_tile(p, "bottom")
  175. end
  176.  
  177. --- The main tile algo, on top.
  178. -- @param screen The screen number to tile.
  179. tile.top = {}
  180. tile.top.name = "tiletop"
  181. function tile.top.arrange(p)
  182.     return do_tile(p, "top")
  183. end
  184.  
  185. tile.arrange = tile.right.arrange
  186. tile.name = tile.right.name
  187.  
  188. return tile