WMII-like tag management
From awesome
[edit] WMII - like tag management
Note: You can also accomplish the below more easily with the Shifty library for Awesome.
WMII [1] deals with tags in a bit more dynamic way than awesome does. On the beginning of session only one tag is created (named '1'). If we enter a tag that didn't exist before, it's created, when we leave an empty tag, it's destroyed.
It's a behaviour that I (a WMII refugee) missed a lot. Luckly it was not too hard to implement with some lua code and help of Garoth and psychon on IRC.
Note: all bits and pieces of the config file are for awesome 3.3-rc2. Other versions may require some tweaking.
First, we need some utility functions we'll use later on:
function dictlen(tbl)
local result = 0
for _, __ in pairs(tbl) do
result = result + 1
end
return result
end
function get_keys(tbl)
local result = {}
for key, _ in pairs(tbl) do
if key then table.insert(result, key) end
end
return result
end
function make_completer(choices)
return function(cmd, cur_pos, ncomp)
local matches = {}
-- abort completion under certain circumstances
if #cmd == 0 or (cur_pos ~= #cmd + 1 and cmd:sub(cur_pos, cur_pos) ~= " ") then
return cmd, cur_pos
end
-- match
for _, match in pairs(choices) do
if match:find("^" .. cmd:sub(1,cur_pos)) then
table.insert(matches, match)
end
end
-- if there are no matches
if #matches == 0 then
return cmd, cur_pos
end
-- cycle
while ncomp > #matches do
ncomp = ncomp - #matches
end
-- return match and position
return matches[ncomp], cur_pos
end
end
Replace loop that add MOD+number keybindings (it's shown below):
-- Compute the maximum number of digit we need, limited to 9
keynumber = 0
for s = 1, screen.count() do
keynumber = math.min(9, math.max(#tags[s], keynumber));
end
for i = 1, keynumber do
table.foreach(awful.key({ modkey }, i,
function () -- [cut]
end), function(_, k) table.insert(globalkeys, k) end)
-- [cut]
end
with something like this:
for i = 0, 9 do
table.foreach(awful.key({ modkey }, i,
function ()
local screen = mouse.screen
awful.tag.viewonly(get_tag(screen, i))
end), function(_, k) table.insert(globalkeys, k) end)
table.foreach(awful.key({ modkey, "Control" }, i,
function ()
local screen = mouse.screen
local tag = get_tag(screen, i)
if tag then
tag.selected = not tag.selected
end
end), function(_, k) table.insert(globalkeys, k) end)
table.foreach(awful.key({ modkey, "Shift" }, i,
function ()
if client.focus then
awful.client.movetotag(get_tag(client.focus.screen, i))
end
end), function(_, k) table.insert(globalkeys, k) end)
table.foreach(awful.key({ modkey, "Control", "Shift" }, i,
function ()
if client.focus then
awful.client.toggletag(get_tag(client.focus.screen, i))
end
end), function(_, k) table.insert(globalkeys, k) end)
if i > 0 then
table.foreach(awful.key({ modkey, "Shift" }, "F" .. i,
function ()
local screen = mouse.screen
if tags[screen][i] then
for k, c in pairs(awful.client.getmarked()) do
awful.client.movetotag(get_tag(screen, i), c)
end
end
end), function(_, k) table.insert(globalkeys, k) end)
end
end
Having that we can move on and create some tag-manipulating functions. Replace all of the "-- {{{ Tags" fold in your rc.lua with this:
-- {{{ Tags
-- Define tags table.
tags = {}
-- put all clients with no tags to "0" tag on screen 1
function handle_orphans()
for _, c in pairs(client.get()) do
if #c:tags() == 0 then
c:tags({get_tag(1, "0")})
end
end
end
function sort_tags(screen_no)
local all_tags = screen[screen_no]:tags()
table.sort(all_tags, function (a, b) return a.name < b.name end)
screen[screen_no]:tags(all_tags)
end
function delete_tag(screen_no, name)
local strname = .. name
if protected_tag == screen_no .. strname then return end
local result = tags[screen_no][strname]
if result ~= nil then
result.screen = nil
tags[screen_no][strname] = nil
end
end
-- gets or creates tag
function get_tag(screen_no, name)
local strname = .. name
local result = tags[screen_no][strname]
if not tags[screen_no][strname] then
protected_tag = screen_no .. strname
result = tag(strname)
result.screen = screen_no
tags[screen_no][strname] = result
awful.layout.set(layouts[1], result)
end
protected_tag = nil
return result
end
for s = 1, screen.count() do
-- Each screen has its own tag table.
tags[s] = {}
get_tag(s, 1).selected = true
end
-- }}}
To make unused tags disappear and have them nicely sorted, add the following
at the end of awful.hooks.arrange.register hook
if screen == mouse.screen then
for n, t in pairs(tags[screen]) do
if #t:clients() == 0 and t ~= awful.tag.selected() and dictlen(tags[screen]) > 1 then
delete_tag(screen, n)
end
end
end
sort_tags(screen)
handle_orphans()
Now, let's have a prompt for our tag manipulation:
function tag_prompt(txt, callback)
return function()
local screen = client.focus and client.focus.screen or mouse.screen
awful.prompt.run({ prompt = txt },
mypromptbox[screen].widget,
function(t) callback(screen, t) end,
make_completer(get_keys(tags[screen])),
awful.util.getdir("cache") .. "/history_tags")
end
end
Let's bind it to keys (check if those bindings conflict with ones you already have!)
awful.key({ modkey, }, "t", tag_prompt("go to tag: ", function (screen, tag) awful.tag.viewonly(get_tag(screen, tag)) end)),
awful.key({ modkey, "Shift" }, "t", tag_prompt("move to tag: ", function (screen, tag) awful.client.movetotag(get_tag(screen, tag)) end)),
awful.key({ modkey, "Ctrl", "Shift" }, "t", tag_prompt("toggle tag: ", function (screen, tag) awful.tag.toggletag(get_tag(screen, tag)) end))
That's it, enjoy!
[edit] Further work
There are some obvious places that you can change/improve this, like:
* +tag and -tag should be supported * dmenu can be used for tag selection instead of awesome's prompt for more WMII - like experience
[edit] Working example
You can download my rc.lua that uses stuff described above from here: [2]

