diff --git a/lib/awful/client.lua.in b/lib/awful/client.lua.in index 65103a87..77cd7e62 100644 --- a/lib/awful/client.lua.in +++ b/lib/awful/client.lua.in @@ -593,6 +593,144 @@ function floating.delete(c) data.floating[c] = nil end +-- Normalize a set of numbers to 1 +-- @param set the set of numbers to normalize +-- @param num the number of numbers to normalize +local function normalize(set, num) + local num = num or #set + local total = 0 + if num then + for i = 1,num do + total = total + set[i] + end + for i = 1,num do + set[i] = set[i] / total + end + else + for i,v in ipairs(set) do + total = total + v + end + + for i,v in ipairs(set) do + set[i] = v / total + end + end +end + +--- Calculate a client's column number, index in that column, and +-- number of visible clients in this column. +-- @param c the client +-- @return col the column number +-- @return idx index of the client in the column +-- @return num the number of visible clients in the column +local function idx(c) + local c = c or capi.client.focus + if not c then return end + + local clients = tiled(c.screen) + local idx = nil + for k, cl in ipairs(clients) do + if cl == c then + idx = k + break + end + end + + local nmaster = tag.getnmaster(t) + if idx <= nmaster then + return {idx = idx, col=0, num=nmaster} + end + local nother = #clients - nmaster + idx = idx - nmaster + + -- rather than regenerate the column number we can calculate it + -- based on the how the tiling algorithm places clients we calculate + -- the column, we could easily use the for loop in the program but we can + -- calculate it. + local ncol = tag.getncol(t) + -- minimum number of clients per column + local percol = math.floor(nother / ncol) + -- number of columns with an extra client + local overcol = math.mod(nother, ncol) + -- number of columns filled with [percol] clients + local regcol = ncol - overcol + + local col = math.floor( (idx - 1) / percol) + 1 + if col > regcol then + -- col = math.floor( (idx - (percol*regcol) - 1) / (percol + 1) ) + regcol + 1 + -- simplified + col = math.floor( (idx + regcol + percol) / (percol+1) ) + -- calculate the index in the column + idx = idx - percol*regcol - (col - regcol - 1) * (percol+1) + percol = percol+1 + else + idx = idx - percol*(col-1) + end + + return {idx = idx, col=col, num=percol} +end + + +--- Set the window factor of a client +-- @param wfact the window factor value +-- @param c the client +function setwfact(wfact, c) + -- get the currently selected window + local c = c or capi.client.focus + if not c then return end + + local t = tag.selected(c.screen) + local w = idx(c) + + local cls = tiled(t.screen) + local nmaster = tag.getnmaster(t) + + -- n is the number of windows currently visible for which we have to be concerned with the properties + local data = tag.getproperty(t, "windowfact") or {} + local colfact = data[w.col] + + colfact[w.idx] = wfact + rest = 1-wfact + + -- calculate the current denominator + local total = 0 + for i = 1,w.num do + if i ~= w.idx then + total = total + colfact[i] + end + end + + -- normalize the windows + for i = 1,w.num do + if i ~= w.idx then + colfact[i] = (colfact[i] * rest) / total + end + end +end + +--- Increment a client's window factor +-- @param add amount to increase the client's window +-- @param c the client +function incwfact(add, c) + local c = c or capi.client.focus + if not c then return end + + local t = tag.selected(c.screen) + + local w = idx(c) + + local nmaster = tag.getnmaster(t) + local data = tag.getproperty(t, "windowfact") or {} + local colfact = data[w.col] + curr = colfact[w.idx] or 1 + colfact[w.idx] = curr + add + + -- keep our ratios normalized + normalize(colfact, w.num) + capi.hooks.arrange()(t.screen) +end + + -- Register standards hooks hooks.focus.register(focus.history.add) hooks.unmanage.register(focus.history.delete) diff --git a/lib/awful/layout/init.lua.in b/lib/awful/layout/init.lua.in index bf457638..c4b7fd9f 100644 --- a/lib/awful/layout/init.lua.in +++ b/lib/awful/layout/init.lua.in @@ -70,6 +70,10 @@ local layouts_name = [suit.tile.left] = "tileleft", [suit.tile.bottom] = "tilebottom", [suit.tile.top] = "tiletop", + [suit.vile] = "vile", + [suit.vile.left] = "vileleft", + [suit.vile.bottom] = "vilebottom", + [suit.vile.top] = "viletop", [suit.fair] = "fairv", [suit.fair.horizontal] = "fairh", [suit.max] = "max", diff --git a/lib/awful/layout/suit/init.lua.in b/lib/awful/layout/suit/init.lua.in index ae83edf4..8c57bf70 100644 --- a/lib/awful/layout/suit/init.lua.in +++ b/lib/awful/layout/suit/init.lua.in @@ -1,5 +1,6 @@ require("awful.layout.suit.max") require("awful.layout.suit.tile") +require("awful.layout.suit.vile") require("awful.layout.suit.fair") require("awful.layout.suit.floating") require("awful.layout.suit.magnifier") diff --git a/lib/awful/layout/suit/vile.lua.in b/lib/awful/layout/suit/vile.lua.in new file mode 100644 index 00000000..e3f1e5c2 --- /dev/null +++ b/lib/awful/layout/suit/vile.lua.in @@ -0,0 +1,177 @@ +--------------------------------------------------------------------------- +-- @author Donald Ephraim Curtis <dcurtis@cs.uiowa.edu> +-- @author Julien Danjou <julien@danjou.info> +-- @copyright 2009 Donald Ephraim Curtis +-- @copyright 2008 Julien Danjou +-- @release @AWESOME_VERSION@ +--------------------------------------------------------------------------- + +-- Grab environment we need +local setmetatable = setmetatable +local ipairs = ipairs +local math = math +local client = require("awful.client") +local tag = require("awful.tag") +local capi = +{ + screen = screen +} + +--- Tiled layouts module for awful +module("awful.layout.suit.vile") + +local function tile_group(cls, wa, orientation, fact, group) + -- get our orientation right + local height = "height" + local width = "width" + local x = "x" + local y = "y" + if orientation == "top" or orientation == "bottom" then + height = "width" + width = "height" + x = "y" + y = "x" + end + + -- make this more generic (not just width) + available = wa[width] - (group.coord - wa[x]) + + -- find our total values + local total_fact = 0 + local min_fact = 1 + local size = group.size + for c = group.first,group.last do + -- determine the width/height based on the size_hint + local i = c - group.first +1 + local size_hints = cls[c].size_hints + local size_hint = size_hints["min_"..width] or size_hints["base_"..width] or 0 + size_hint = size_hint + cls[c].border_width*2 + size = math.max(size_hint, size) + + -- calculate the height + if not fact[i] then + fact[i] = min_fact + else + min_fact = math.min(fact[i],min_fact) + end + total_fact = total_fact + fact[i] + end + size = math.min(size, available) + + local coord = wa[y] + local geom = {} + local used_size = 0 + local unused = wa[height] + for c = group.first,group.last do + local i = c - group.first +1 + geom[width] = size + geom[height] = math.floor(unused * fact[i] / total_fact) + geom[x] = group.coord + geom[y] = coord + geom = cls[c]:geometry(geom) + coord = coord + geom[height] + unused = unused - geom[height] + total_fact = total_fact - fact[i] + used_size = math.max(used_size, geom[width]) + end + + return used_size + +end + +local function vile(_, screen, orientation) + orientation = orientation or "right" + + -- this handles are different orientations + local height = "height" + local width = "width" + local x = "x" + local y = "y" + if orientation == "top" or orientation == "bottom" then + height = "width" + width = "height" + x = "y" + y = "x" + end + + local t = tag.selected(screen) + local cls = client.tiled(screen) + local nmaster = math.min(tag.getnmaster(t), #cls) + local nother = math.max(#cls - nmaster,0) + + local mwfact = tag.getmwfact(t) + local wa = capi.screen[screen].workarea + local ncol = tag.getncol(t) + + local data = tag.getproperty(t,"windowfact") + + if not data then + data = {} + tag.setproperty(t,"windowfact", data) + end + + -- + local coord = wa[x] + local place_master = true + if orientation == "left" or orientation == "top" then + -- if we are on the left or top we need to render the other windows first + place_master = false + end + + -- this was easier than writing functions because there is a lot of data we need + for d = 1,2 do + if place_master and nmaster > 0 then + local size = wa[width] + if nother > 0 then + size = math.min(wa[width] * mwfact, wa[width] - (coord - wa[x])) + end + if not data[0] then + data[0] = {} + end + coord = coord + tile_group(cls, wa, orientation, data[0], {first=1, last=nmaster, coord = coord, size = size}) + end + + if not place_master and nother > 0 then + local last = nmaster + + -- we have to modify the work area size to consider left and top views + local wasize = wa[width] + if nmaster > 0 and (orientation == "left" or orientation == "top") then + wasize = wa[width] - wa[width]*mwfact + end + for i = 1,ncol do + -- Try to get equal width among remaining columns + local size = math.min( (wasize - (coord - wa[x])) / (ncol - i + 1) ) + local first = last + 1 + last = last + math.floor((#cls - last)/(ncol - i + 1)) + -- tile the column and update our current x coordinate + if not data[i] then + data[i] = {} + end + coord = coord + tile_group(cls, wa, orientation, data[i], { first = first, last = last, coord = coord, size = size }) + end + end + place_master = not place_master + end + +end + +--- The main tile algo, on left. +-- @param screen The screen number to tile. +function left(screen) + return vile(nil, screen, "left") +end + +--- The main tile algo, on bottom. +-- @param screen The screen number to tile. +function bottom(screen) + return vile(nil, screen, "bottom") +end + +--- The main tile algo, on top. +-- @param screen The screen number to tile. +function top(screen) + return vile(nil, screen, "top") +end + +setmetatable(_M, { __call = vile }) diff --git a/lib/awful/mouse.lua.in b/lib/awful/mouse.lua.in index 43905067..29d2f8da 100644 --- a/lib/awful/mouse.lua.in +++ b/lib/awful/mouse.lua.in @@ -392,6 +392,100 @@ local function client_resize_tiled(c, lay) end, cursor) end +local function client_resize_viled(c, lay) + local wa = capi.screen[c.screen].workarea + local mwfact = tag.getmwfact() + local cursor + local g = c:geometry() + local offset = 0 + local x,y + if lay == layout.suit.vile then + cursor = "cross" + if g.height+15 > wa.height then + offset = g.height * .5 + cursor = "sb_h_double_arrow" + elseif not (g.y+g.height+15 > wa.y+wa.height) then + offset = g.height + end + capi.mouse.coords({ x = wa.x + wa.width * mwfact, y = g.y + offset }) + elseif lay == layout.suit.vile.left then + cursor = "cross" + if g.height+15 >= wa.height then + offset = g.height * .5 + cursor = "sb_h_double_arrow" + elseif not (g.y+g.height+15 > wa.y+wa.height) then + offset = g.height + end + capi.mouse.coords({ x = wa.x + wa.width * (1 - mwfact), y = g.y + offset }) + elseif lay == layout.suit.vile.bottom then + cursor = "cross" + if g.width+15 >= wa.width then + offset = g.width * .5 + cursor = "sb_v_double_arrow" + elseif not (g.x+g.width+15 > wa.x+wa.width) then + offset = g.width + end + capi.mouse.coords({ y = wa.y + wa.height * mwfact, x = g.x + offset}) + else + cursor = "cross" + if g.width+15 >= wa.width then + offset = g.width * .5 + cursor = "sb_v_double_arrow" + elseif not (g.x+g.width+15 > wa.x+wa.width) then + offset = g.width + end + capi.mouse.coords({ y = wa.y + wa.height * (1 - mwfact), x= g.x + offset }) + end + + capi.mousegrabber.run(function (mouse) + for k, v in ipairs(mouse.buttons) do + if v then + local fact_x = (mouse.x - wa.x) / wa.width + local fact_y = (mouse.y - wa.y) / wa.height + local mwfact + + local g = c:geometry() + + + -- we have to make sure we're not on the last visible client where we have to use different settings. + local wfact + local wfact_x, wfact_y + if (g.y+g.height+15) > (wa.y+wa.height) then + wfact_y = (g.y + g.height - mouse.y) / wa.height + else + wfact_y = (mouse.y - g.y) / wa.height + end + + if (g.x+g.width+15) > (wa.x+wa.width) then + wfact_x = (g.x + g.width - mouse.x) / wa.width + else + wfact_x = (mouse.x - g.x) / wa.width + end + + + if lay == layout.suit.vile then + mwfact = fact_x + wfact = wfact_y + elseif lay == layout.suit.vile.left then + mwfact = 1 - fact_x + wfact = wfact_y + elseif lay == layout.suit.vile.bottom then + mwfact = fact_y + wfact = wfact_x + else + mwfact = 1 - fact_y + wfact = wfact_x + end + + tag.setmwfact(math.min(math.max(mwfact, 0.01), 0.99), tag.selected(c.screen)) + aclient.setwfact(math.min(math.max(wfact,0.01), 0.99), c) + return true + end + end + return false + end, cursor) +end + local function client_resize_floating(c, corner, fixed_x, fixed_y) local corner, x, y = client.corner(c, corner) local g = c:geometry() @@ -483,6 +577,12 @@ function client.resize(c, corner) or lay == layout.suit.tile.top or lay == layout.suit.tile.bottom then return client_resize_tiled(c, lay) + elseif lay == layout.suit.vile + or lay == layout.suit.vile.left + or lay == layout.suit.vile.top + or lay == layout.suit.vile.bottom + then + return client_resize_viled(c, lay) elseif lay == layout.suit.magnifier then return client_resize_magnifier(c, corner) end diff --git a/themes/default/layouts/vile.png b/themes/default/layouts/vile.png new file mode 100644 index 00000000..071a385f Binary files /dev/null and b/themes/default/layouts/vile.png differ diff --git a/themes/default/layouts/vilebottom.png b/themes/default/layouts/vilebottom.png new file mode 100644 index 00000000..aeedbe23 Binary files /dev/null and b/themes/default/layouts/vilebottom.png differ diff --git a/themes/default/layouts/vilebottomw.png b/themes/default/layouts/vilebottomw.png new file mode 100644 index 00000000..f42da9dd Binary files /dev/null and b/themes/default/layouts/vilebottomw.png differ diff --git a/themes/default/layouts/vileleft.png b/themes/default/layouts/vileleft.png new file mode 100644 index 00000000..ab55e087 Binary files /dev/null and b/themes/default/layouts/vileleft.png differ diff --git a/themes/default/layouts/vileleftw.png b/themes/default/layouts/vileleftw.png new file mode 100644 index 00000000..c18e7b43 Binary files /dev/null and b/themes/default/layouts/vileleftw.png differ diff --git a/themes/default/layouts/viletop.png b/themes/default/layouts/viletop.png new file mode 100644 index 00000000..3febc350 Binary files /dev/null and b/themes/default/layouts/viletop.png differ diff --git a/themes/default/layouts/viletopw.png b/themes/default/layouts/viletopw.png new file mode 100644 index 00000000..daf965fa Binary files /dev/null and b/themes/default/layouts/viletopw.png differ diff --git a/themes/default/layouts/vilew.png b/themes/default/layouts/vilew.png new file mode 100644 index 00000000..c722a358 Binary files /dev/null and b/themes/default/layouts/vilew.png differ diff --git a/themes/default/theme.in b/themes/default/theme.in index a8b93331..51e6b42f 100644 --- a/themes/default/theme.in +++ b/themes/default/theme.in @@ -60,5 +60,9 @@ layout_tilebottom = @AWESOME_THEMES_PATH@/default/layouts/tilebottomw.png layout_tileleft = @AWESOME_THEMES_PATH@/default/layouts/tileleftw.png layout_tile = @AWESOME_THEMES_PATH@/default/layouts/tilew.png layout_tiletop = @AWESOME_THEMES_PATH@/default/layouts/tiletopw.png +layout_vilebottom = @AWESOME_THEMES_PATH@/default/layouts/vilebottomw.png +layout_vileleft = @AWESOME_THEMES_PATH@/default/layouts/vileleftw.png +layout_vile = @AWESOME_THEMES_PATH@/default/layouts/vilew.png +layout_viletop = @AWESOME_THEMES_PATH@/default/layouts/viletopw.png awesome_icon = @AWESOME_ICON_PATH@/awesome16.png diff --git a/themes/sky/layouts/vile.png b/themes/sky/layouts/vile.png new file mode 100644 index 00000000..550393a2 Binary files /dev/null and b/themes/sky/layouts/vile.png differ diff --git a/themes/sky/layouts/vilebottom.png b/themes/sky/layouts/vilebottom.png new file mode 100644 index 00000000..1dcc2271 Binary files /dev/null and b/themes/sky/layouts/vilebottom.png differ diff --git a/themes/sky/layouts/vileleft.png b/themes/sky/layouts/vileleft.png new file mode 100644 index 00000000..14e13f58 Binary files /dev/null and b/themes/sky/layouts/vileleft.png differ diff --git a/themes/sky/layouts/viletop.png b/themes/sky/layouts/viletop.png new file mode 100644 index 00000000..2bcb161c Binary files /dev/null and b/themes/sky/layouts/viletop.png differ diff --git a/themes/sky/theme.in b/themes/sky/theme.in index f0cb4378..b720c50d 100644 --- a/themes/sky/theme.in +++ b/themes/sky/theme.in @@ -31,6 +31,10 @@ layout_tilebottom = @AWESOME_THEMES_PATH@/sky/layouts/tilebottom.png layout_tileleft = @AWESOME_THEMES_PATH@/sky/layouts/tileleft.png layout_tile = @AWESOME_THEMES_PATH@/sky/layouts/tile.png layout_tiletop = @AWESOME_THEMES_PATH@/sky/layouts/tiletop.png +layout_vilebottom = @AWESOME_THEMES_PATH@/sky/layouts/vilebottom.png +layout_vileleft = @AWESOME_THEMES_PATH@/sky/layouts/vileleft.png +layout_vile = @AWESOME_THEMES_PATH@/sky/layouts/vile.png +layout_viletop = @AWESOME_THEMES_PATH@/sky/layouts/viletop.png awesome_icon = @AWESOME_THEMES_PATH@/sky/awesome-icon.png tasklist_floating_icon = @AWESOME_THEMES_PATH@/sky/layouts/floating.png