From 0ccfea330af7ad25436c6c39a7945e23f5aed3a0 Mon Sep 17 00:00:00 2001 From: Lucas de Vries Date: Wed, 11 Jun 2008 07:47:57 +0200 Subject: [PATCH] [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 --- awesomerc.lua.in | 7 -- awful.lua | 21 +++- tabulous.lua | 288 +++++++++++++++++++++++++++++++---------------- 3 files changed, 211 insertions(+), 105 deletions(-) diff --git a/awesomerc.lua.in b/awesomerc.lua.in index e0b3ae61..a8501de7 100644 --- a/awesomerc.lua.in +++ b/awesomerc.lua.in @@ -2,8 +2,6 @@ -- Include awesome library, with lots of useful function! require("awful") --- Include another library, which defines useful tabular system -require("tabulous") -- {{{ Colors and fonts awesome.font_set("sans 8") @@ -176,11 +174,6 @@ keybinding.new({ modkey, "Shift" }, "space", function () awful.layout.inc(layout -- Menu 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 diff --git a/awful.lua b/awful.lua index af5c75ed..1a7d4880 100644 --- a/awful.lua +++ b/awful.lua @@ -17,6 +17,7 @@ end -- Grab environment we need local ipairs = ipairs local pairs = pairs +local unpack = unpack local awesome = awesome local screen = screen local client = client @@ -26,8 +27,6 @@ local os = os local table = table local hooks = hooks local keygrabber = keygrabber -local print = print -local table = table -- Reset env setfenv(1, P) @@ -303,6 +302,24 @@ end P.hooks = {} 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 if name ~= 'timer' then P.hooks[name] = function (f) diff --git a/tabulous.lua b/tabulous.lua index aa16404a..ad7c31ec 100644 --- a/tabulous.lua +++ b/tabulous.lua @@ -1,8 +1,11 @@ ------------------------------------------------ --- tabulous: Fabulous tabs for awesome -- --- -- --- © 2008 Julien Danjou -- ------------------------------------------------ +---------------------------------------------------- +-- tabulous: Fabulous tabs for awesome -- +-- -- +-- © 2008 Julien Danjou -- +-- © 2008 Lucas de Vries -- +---------------------------------------------------- + +require('awful') -------------------- -- Module loading -- @@ -18,125 +21,218 @@ end local client = client local table = table local pairs = pairs -local rawset = rawset -local setmetatable = setmetatable +local awful = awful -- Reset env 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 = {} --- Define a special index method. --- This is useful because we need to compare clients with __eq() to be sure --- that the key are identical. Otherwise Lua assume that each object is different. -local function tabbed_index(table, key) - 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 }) +awful.hooks.userhook_create('tabbed') +awful.hooks.userhook_create('untabbed') +awful.hooks.userhook_create('tabdisplay') +awful.hooks.userhook_create('tabhide') --------------- -- Functions -- --------------- --- Get the next tab -local function client_nexttab(s) - local sel = s or client.focus_get() - if sel and tabbed[sel] then - return tabbed[sel] - end -end - --- Get the previous tab -local function client_prevtab(c, stopc) - local sel = c or client.focus_get() - local stop = stopc or sel - if sel and tabbed[sel] then - if stop == tabbed[sel] then - return sel - else - return client_prevtab(tabbed[sel], stop) +-- Find the key in a table +local function findkey(table, cl) + for i, c in pairs(table) do + if c == cl then + return i end end + return nil end --- Tab a client with another one -local function client_tab(c1, s) - local sel = s or client.focus_get() - if c1 and sel and c1 ~= sel then - if tabbed[c1] then - tabbed[sel] = tabbed[c1] - tabbed[c1] = sel - elseif tabbed[sel] then - tabbed[c1] = tabbed[sel] - tabbed[sel] = c1 - else - tabbed[c1] = sel - tabbed[sel] = c1 +-- 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 + +-- Check if the client is in the given tabindex +local function client_in_tabindex(tabindex, cl) + local c = cl or client.focus_get() + return findkey(tabbed[tabindex], c) +end + +-- 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 - c1:hide() + 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 --- Untab a client -local function client_untab(c) - local sel = c or client.focus_get() - if sel and tabbed[sel] then - local p = client_prevtab(sel) - tabbed[p] = tabbed[sel] - tabbed[sel] = nil - p:unhide() +-- 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 + return tabbed[tabindex][2][i+1] end end --- Focus the next tab -local function client_focusnexttab(s) - local sel = s or client.focus_get() - local n = client_nexttab(sel) - if n then - sel:hide() - n:unhide() - n:focus_set() +-- Get the previous client in a tabdisplay +local function client_prev(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][table.maxn(tabbed[tabindex][2])] + else + return tabbed[tabindex][2][i-1] end end --- Focus the previous tab -local function client_focusprevtab(s) - local sel = s or client.focus_get() - local p = client_prevtab(sel) - if p then - sel:hide() - p:unhide() - p:focus_set() +-- Remove a client from a tabbed display +local function client_untab(cl) + local c = cl or client.focus_get() + local tabindex = client_find_tabindex(c) + if tabindex == nil then return false end + + local cindex = findkey(tabbed[tabindex][2], c) + + 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 +-- Get all clients on a tabbed display +local function client_get_clients(tabindex) + return tabbed[tabindex][2] +end + +-- Get the displayed client on a tabbed display +local function client_get_displayed(tabindex) + return tabbed[tabindex][1] +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.untab = client_untab -P.focusnext = client_focusnexttab -P.focusprev = client_focusprevtab -P.next = client_nexttab -P.prev = client_prevtab +P.untab_all = client_untab_all + +P.next = client_next +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