[tabulous] Improve

I've attached a patch file which improves on the recently added
tabulous lib a lot, using it will require a bit more config-end logic,
but it's much more powerful and allows for things such as tabbars and
autotabbing.

List of changes:
* [awful] Add awful.hooks.userhook_create and
awful.hooks.userhook_call so external libs can easily add their own
hooks (tabulous uses these)
* Tabulous now uses a tabindex system, instead of a messy circular
table, every set of tabs is now a tabbed view, this is more practical
and allows for the same order to be retained even if the focus shifts
(otherwise, the currently focussed tag would always be the first one
in the tabbar)
* Tabulous now exports a number of extra functions that will help you
in managing your tabs efficiently
* Tabulous now has an autotab_start() function you can call
(preferably right after the require so any other hooks you may have
set up won't interfere with it), when this is on, any newly created
windows will automatically be tabbed into the current tabbed view if
you have one selected (if the focussed window is not part of a tabbed
view, tabulous will do nothing)
* Tabulous bugfixes

Signed-off-by: Julien Danjou <julien@danjou.info>
This commit is contained in:
Lucas de Vries 2008-06-11 07:47:57 +02:00 committed by Julien Danjou
parent 11e93c2dbf
commit 0ccfea330a
3 changed files with 211 additions and 105 deletions

View File

@ -2,8 +2,6 @@
-- Include awesome library, with lots of useful function! -- Include awesome library, with lots of useful function!
require("awful") require("awful")
-- Include another library, which defines useful tabular system
require("tabulous")
-- {{{ Colors and fonts -- {{{ Colors and fonts
awesome.font_set("sans 8") awesome.font_set("sans 8")
@ -176,11 +174,6 @@ keybinding.new({ modkey, "Shift" }, "space", function () awful.layout.inc(layout
-- Menu -- Menu
keybinding.new({ modkey }, "F1", function () awful.menu("Run: ", mymenubox, awful.spawn) end):add() keybinding.new({ modkey }, "F1", function () awful.menu("Run: ", mymenubox, awful.spawn) end):add()
-- Tabulous, tab manipulation
keybinding.new({ modkey, "Control" }, "y", function () tabulous.tab(awful.client.next(1)) end):add()
keybinding.new({ modkey, "Shift" }, "y", tabulous.untab):add()
keybinding.new({ modkey }, "y", tabulous.focusnext):add()
-- }}} -- }}}
-- {{{ Hooks -- {{{ Hooks

View File

@ -17,6 +17,7 @@ end
-- Grab environment we need -- Grab environment we need
local ipairs = ipairs local ipairs = ipairs
local pairs = pairs local pairs = pairs
local unpack = unpack
local awesome = awesome local awesome = awesome
local screen = screen local screen = screen
local client = client local client = client
@ -26,8 +27,6 @@ local os = os
local table = table local table = table
local hooks = hooks local hooks = hooks
local keygrabber = keygrabber local keygrabber = keygrabber
local print = print
local table = table
-- Reset env -- Reset env
setfenv(1, P) setfenv(1, P)
@ -303,6 +302,24 @@ end
P.hooks = {} P.hooks = {}
P.myhooks = {} P.myhooks = {}
-- Create a new userhook (for external libs)
local function userhook_create(name)
P.myhooks[name] = {}
P.hooks[name] = function (f)
table.insert(P.myhooks[name], {callback = f})
end
end
-- Call a created userhook (for external libs
local function userhook_call(name, args)
for i,o in pairs(P.myhooks[name]) do
P.myhooks[name][i]['callback'](unpack(args))
end
end
P.hooks['userhook_create'] = userhook_create
P.hooks['userhook_call'] = userhook_call
for name, hook in pairs(hooks) do for name, hook in pairs(hooks) do
if name ~= 'timer' then if name ~= 'timer' then
P.hooks[name] = function (f) P.hooks[name] = function (f)

View File

@ -1,8 +1,11 @@
----------------------------------------------- ----------------------------------------------------
-- tabulous: Fabulous tabs for awesome -- -- tabulous: Fabulous tabs for awesome --
-- -- -- --
-- © 2008 Julien Danjou <julien@danjou.info> -- -- © 2008 Julien Danjou <julien@danjou.info> --
----------------------------------------------- -- © 2008 Lucas de Vries <lucasdevries@gmail.com> --
----------------------------------------------------
require('awful')
-------------------- --------------------
-- Module loading -- -- Module loading --
@ -18,125 +21,218 @@ end
local client = client local client = client
local table = table local table = table
local pairs = pairs local pairs = pairs
local rawset = rawset local awful = awful
local setmetatable = setmetatable
-- Reset env -- Reset env
setfenv(1, P) setfenv(1, P)
-- The tab datastructure.
-- It contains keys which are clients. If a c1 is a client,
-- then if tabbed[c1] exists it must contains a client c2.
-- And if so, tabbed[c2] must contain c1.
-- It's cycling i.e. tabbed[c1] = c2, tabbed[c2] = c3, tabbed[c3] = c1
local tabbed = {} local tabbed = {}
-- Define a special index method. awful.hooks.userhook_create('tabbed')
-- This is useful because we need to compare clients with __eq() to be sure awful.hooks.userhook_create('untabbed')
-- that the key are identical. Otherwise Lua assume that each object is different. awful.hooks.userhook_create('tabdisplay')
local function tabbed_index(table, key) awful.hooks.userhook_create('tabhide')
for k, v in pairs(table) do
if k == key then
return v
end
end
return nil
end
-- Same as tabbed_index, cheat!
local function tabbed_newindex(table, key, value)
for k, v in pairs(table) do
if k == key then
rawset(table, k, value)
return
end
end
rawset(table, key, value)
end
-- Set the metatable for tabbed
setmetatable(tabbed, { __index = tabbed_index, __newindex = tabbed_newindex })
--------------- ---------------
-- Functions -- -- Functions --
--------------- ---------------
-- Get the next tab -- Find the key in a table
local function client_nexttab(s) local function findkey(table, cl)
local sel = s or client.focus_get() for i, c in pairs(table) do
if sel and tabbed[sel] then if c == cl then
return tabbed[sel] return i
end
end
return nil
end
-- Swap which tab is displayed
local function client_display(tabindex, cl)
local p = tabbed[tabindex][1]
if cl and p ~= cl then
cl:unhide()
cl:swap(p)
p:hide()
cl:focus_set()
tabbed[tabindex][1] = cl
awful.hooks.userhook_call('tabhide', {p})
awful.hooks.userhook_call('tabdisplay', {cl})
end end
end end
-- Get the previous tab -- Check if the client is in the given tabindex
local function client_prevtab(c, stopc) local function client_in_tabindex(tabindex, cl)
local sel = c or client.focus_get() local c = cl or client.focus_get()
local stop = stopc or sel return findkey(tabbed[tabindex], c)
if sel and tabbed[sel] then end
if stop == tabbed[sel] then
return sel -- Find the tab index or return nil if not tabbed
local function client_find_tabindex(cl)
local c = cl or client.focus_get()
for tabindex, tabdisplay in pairs(tabbed) do
-- Loop through all tab displays
local me = client_in_tabindex(tabindex, c)
if me ~= nil then
return tabindex
end
end
return nil
end
-- Create a new tabbed display with client as the master
local function client_tab_create(cl)
local c = cl or client.focus_get()
table.insert(tabbed, {
c, -- Window currently being displayed
{ c } -- List of windows in tabbed display
})
awful.hooks.userhook_call('tabbed', {c})
return client_find_tabindex(c)
end
-- Add a client to a tabbed display
local function client_tab(tabindex, cl)
local c = cl or client.focus_get()
if tabbed[tabindex] ~= nil and client_find_tabindex(c) == nil then
table.insert(tabbed[tabindex][2], c)
client_display(tabindex, c)
awful.hooks.userhook_call('tabbed', {c})
end
end
-- Get the next client in a tabdisplay
local function client_next(tabindex, cl)
local c = cl or tabbed[tabindex][1]
local i = findkey(tabbed[tabindex][2], c)
if i == nil then
return nil
end
if tabbed[tabindex][2][i+1] == nil then
return tabbed[tabindex][2][1]
else else
return client_prevtab(tabbed[sel], stop) return tabbed[tabindex][2][i+1]
end
end end
end end
-- Tab a client with another one -- Get the previous client in a tabdisplay
local function client_tab(c1, s) local function client_prev(tabindex, cl)
local sel = s or client.focus_get() local c = cl or tabbed[tabindex][1]
if c1 and sel and c1 ~= sel then
if tabbed[c1] then local i = findkey(tabbed[tabindex][2], c)
tabbed[sel] = tabbed[c1]
tabbed[c1] = sel if i == nil then
elseif tabbed[sel] then return nil
tabbed[c1] = tabbed[sel] end
tabbed[sel] = c1
if tabbed[tabindex][2][i-1] == nil then
return tabbed[tabindex][2][table.maxn(tabbed[tabindex][2])]
else else
tabbed[c1] = sel return tabbed[tabindex][2][i-1]
tabbed[sel] = c1
end
c1:hide()
end end
end end
-- Untab a client -- Remove a client from a tabbed display
local function client_untab(c) local function client_untab(cl)
local sel = c or client.focus_get() local c = cl or client.focus_get()
if sel and tabbed[sel] then local tabindex = client_find_tabindex(c)
local p = client_prevtab(sel) if tabindex == nil then return false end
tabbed[p] = tabbed[sel]
tabbed[sel] = nil local cindex = findkey(tabbed[tabindex][2], c)
p:unhide()
if tabbed[tabindex][1] == c then
client_display(tabindex, client_next(tabindex, c))
end
table.remove(tabbed[tabindex][2], cindex)
if table.maxn(tabbed[tabindex][2]) == o then
-- Table is empty now, remove the tabbed display
table.remove(tabbed, tabindex)
end
c:unhide()
awful.hooks.userhook_call('untabbed', {c})
end
-- Untab all clients in a tabbed display
local function client_untab_all(tabindex)
for i,c in pairs(tabbed[tabindex][2]) do
awful.hooks.userhook_call('untabbed', {c})
end
if tabbed[tabindex] ~= nil then
table.remove(tabbed, tabindex)
end end
end end
-- Focus the next tab -- Get all clients on a tabbed display
local function client_focusnexttab(s) local function client_get_clients(tabindex)
local sel = s or client.focus_get() return tabbed[tabindex][2]
local n = client_nexttab(sel)
if n then
sel:hide()
n:unhide()
n:focus_set()
end
end end
-- Focus the previous tab -- Get the displayed client on a tabbed display
local function client_focusprevtab(s) local function client_get_displayed(tabindex)
local sel = s or client.focus_get() return tabbed[tabindex][1]
local p = client_prevtab(sel)
if p then
sel:hide()
p:unhide()
p:focus_set()
end
end end
-- Get a client by tab number
local function client_get_position(tabindex, pos)
if tabbed[tabindex] == nil then return nil end
return tabbed[tabindex][2][pos]
end
-- Start autotabbing, this automatically
-- tabs new clients when the current client
-- is tabbed
local function autotab_start()
awful.hooks.manage(function (c)
local sel = client.focus_get()
local index = client_find_tabindex(sel)
if index ~= nil then
-- Currently focussed client is tabbed,
-- add the new window to the tabbed display
client_tab(index, c)
end
end)
end
-- Set up hook so we don't leave lost hidden clients
awful.hooks.unmanage(function (c) client_untab(c) end)
P.findkey = findkey
P.display = client_display
P.tabindex_check = client_in_tabindex
P.tabindex_get = client_find_tabindex
P.tab_create = client_tab_create
P.tab = client_tab P.tab = client_tab
P.untab = client_untab P.untab = client_untab
P.focusnext = client_focusnexttab P.untab_all = client_untab_all
P.focusprev = client_focusprevtab
P.next = client_nexttab P.next = client_next
P.prev = client_prevtab P.prev = client_prev
P.clients_get = client_get_clients
P.displayed_get = client_get_displayed
P.position_get = client_get_position
P.autotab_start = autotab_start
P.tabbed = tabbed
return P return P