diff --git a/lib/awful/widget/common.lua.in b/lib/awful/widget/common.lua.in
index 6631ccee..8e2af433 100644
--- a/lib/awful/widget/common.lua.in
+++ b/lib/awful/widget/common.lua.in
@@ -8,48 +8,70 @@
local math = math
local type = type
local ipairs = ipairs
+local pairs = pairs
local setmetatable = setmetatable
local capi = { widget = widget, button = button }
+local layout = require("awful.widget.layout")
+local util = require("awful.util")
---- Common widget code
-module("awful.widget.common")
+local common = {}
-- Private structures
tagwidgets = setmetatable({}, { __mode = 'k' })
-function list_update(w, buttons, label, data, widgets, objects)
+-- Recursively processes a template, replacing the tables representing the icon and
+-- the title with the widgets ib and tb
+local function replace_in_template(t, ib, tb)
+ for i, v in ipairs(t) do
+ if type(t[i]) == "table" then
+ if v.item == "icon" then
+ t[i] = ib
+ elseif v.item == "title" then
+ tb.align = v.align or "left"
+ tb:margin(v.margin or {})
+ tb.bg_resize = v.bg_resize or false
+ tb.bg_align = v.bg_align or ""
+ t[i] = tb
+ else
+ replace_in_template(v, ib, tb)
+ end
+ end
+ end
+end
+
+function common.list_update(w, buttons, label, data, template, objects)
-- Hack: if it has been registered as a widget in a wibox,
-- it's w.len since __len meta does not work on table until Lua 5.2.
-- Otherwise it's standard #w.
- local len = (w.len or #w) / 2
- -- Add more widgets
- if len < #objects then
- for i = len * 2 + 1, #objects * 2, 2 do
- local ib = capi.widget({ type = "imagebox", align = widgets.imagebox.align })
- local tb = capi.widget({ type = "textbox", align = widgets.textbox.align })
+ local len = (w.len or #w)
- w[i] = ib
- w[i + 1] = tb
- w[i + 1]:margin({ left = widgets.textbox.margin.left, right = widgets.textbox.margin.right })
- w[i + 1].bg_resize = widgets.textbox.bg_resize or false
- w[i + 1].bg_align = widgets.textbox.bg_align or ""
-
- if type(objects[math.floor(i / 2) + 1]) == "tag" then
- tagwidgets[ib] = objects[math.floor(i / 2) + 1]
- tagwidgets[tb] = objects[math.floor(i / 2) + 1]
- end
- end
- -- Remove widgets
- elseif len > #objects then
- for i = #objects * 2 + 1, len * 2, 2 do
+ -- Remove excessive widgets
+ if len > #objects then
+ for i = #objects, len do
w[i] = nil
- w[i + 1] = nil
end
end
- -- update widgets text
- for k = 1, #objects * 2, 2 do
- local o = objects[(k + 1) / 2]
+ -- update the widgets, creating them if needed
+ for i, o in ipairs(objects) do
+ local ib, tb
+ if w[i] then
+ ib = w[i].icon
+ tb = w[i].title
+ else
+ ib = capi.widget({ type = "imagebox" })
+ tb = capi.widget({ type = "textbox" })
+ w[i] = util.table.clone(template)
+ replace_in_template(w[i], ib, tb)
+ w[i].icon = ib
+ w[i].title = tb
+
+ if type(o) == "tag" then
+ tagwidgets[ib] = o
+ tagwidgets[tb] = o
+ end
+ end
+
if buttons then
if not data[o] then
data[o] = { }
@@ -64,24 +86,26 @@ function list_update(w, buttons, label, data, widgets, objects)
data[o][#data[o] + 1] = btn
end
end
- w[k]:buttons(data[o])
- w[k + 1]:buttons(data[o])
+ ib:buttons(data[o])
+ tb:buttons(data[o])
end
local text, bg, bg_image, icon = label(o)
- w[k + 1].text, w[k + 1].bg, w[k + 1].bg_image = text, bg, bg_image
- w[k].bg, w[k].image = bg, icon
- if not w[k + 1].text then
- w[k+1].visible = false
+ tb.text, tb.bg, tb.bg_image = text, bg, bg_image
+ ib.bg, ib.image = bg, icon
+ if not tb.text then
+ tb.visible = false
else
- w[k+1].visible = true
+ tb.visible = true
end
- if not w[k].image then
- w[k].visible = false
+ if not ib.image then
+ ib.visible = false
else
- w[k].visible = true
+ ib.visible = true
end
end
end
+return common
+
-- vim: filetype=lua:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:encoding=utf-8:textwidth=80
diff --git a/lib/awful/widget/taglist.lua.in b/lib/awful/widget/taglist.lua.in
index 5b283fc5..7c83017d 100644
--- a/lib/awful/widget/taglist.lua.in
+++ b/lib/awful/widget/taglist.lua.in
@@ -23,89 +23,9 @@ local layout = require("awful.widget.layout")
--- Taglist widget module for awful
module("awful.widget.taglist")
-label = {}
+filter = {}
-local function taglist_update (screen, w, label, buttons, data, widgets)
- local tags = capi.screen[screen]:tags()
- local showntags = {}
- for k, t in ipairs(tags) do
- if not tag.getproperty(t, "hide") then
- table.insert(showntags, t)
- end
- end
- common.list_update(w, buttons, label, data, widgets, showntags)
-end
-
---- Get the tag object the given widget appears on.
--- @param widget The widget the look for.
--- @return The tag object.
-function gettag(widget)
- return common.tagwidgets[widget]
-end
-
---- Create a new taglist widget.
--- @param screen The screen to draw tag list for.
--- @param label Label function to use.
--- @param buttons A table with buttons binding to set.
-function new(screen, label, buttons)
- local w = {
- layout = layout.horizontal.leftright
- }
- local widgets = { }
- widgets.imagebox = { }
- widgets.textbox = { ["margin"] = { ["left"] = 0,
- ["right"] = 0},
- ["bg_resize"] = true
- }
- local data = setmetatable({}, { __mode = 'k' })
- local u = function (s)
- if s == screen then
- taglist_update(s, w, label, buttons, data, widgets)
- end
- end
- local uc = function (c) return u(c.screen) end
- capi.client.add_signal("focus", uc)
- capi.client.add_signal("unfocus", uc)
- tag.attached_add_signal(screen, "property::selected", uc)
- tag.attached_add_signal(screen, "property::icon", uc)
- tag.attached_add_signal(screen, "property::hide", uc)
- tag.attached_add_signal(screen, "property::name", uc)
- capi.screen[screen]:add_signal("tag::attach", function(screen, tag)
- u(screen.index)
- end)
- capi.screen[screen]:add_signal("tag::detach", function(screen, tag)
- u(screen.index)
- end)
- capi.client.add_signal("new", function(c)
- c:add_signal("property::urgent", uc)
- c:add_signal("property::screen", function(c)
- -- If client change screen, refresh it anyway since we don't from
- -- which screen it was coming :-)
- u(screen)
- end)
- c:add_signal("tagged", uc)
- c:add_signal("untagged", uc)
- end)
- capi.client.add_signal("unmanage", uc)
- u(screen)
- return w
-end
-
---- Return labels for a taglist widget with all tag from screen.
--- It returns the tag name and set a special
--- foreground and background color for selected tags.
--- @param t The tag.
--- @param args The arguments table.
--- bg_focus The background color for selected tag.
--- fg_focus The foreground color for selected tag.
--- bg_urgent The background color for urgent tags.
--- fg_urgent The foreground color for urgent tags.
--- squares_sel Optional: a user provided image for selected squares.
--- squares_unsel Optional: a user provided image for unselected squares.
--- squares_resize Optional: true or false to resize squares.
--- @return A string to print, a background color, a background image and a
--- background resize value.
-function label.all(t, args)
+function taglist_label(t, args)
if not args then args = {} end
local theme = beautiful.get()
local fg_focus = args.fg_focus or theme.taglist_fg_focus or theme.fg_focus
@@ -174,21 +94,122 @@ function label.all(t, args)
return text, bg_color, bg_image, icon
end
---- Return labels for a taglist widget with all *non empty* tags from screen.
--- It returns the tag name and set a special
--- foreground and background color for selected tags.
--- @param t The tag.
--- @param args The arguments table.
--- bg_focus The background color for selected tag.
--- fg_focus The foreground color for selected tag.
--- bg_urgent The background color for urgent tags.
--- fg_urgent The foreground color for urgent tags.
--- @return A string to print, a background color, a background image and a
--- background resize value.
-function label.noempty(t, args)
- if #t:clients() > 0 or t.selected then
- return label.all(t, args)
+local function taglist_update(s, w, buttons, filter, data, style, template)
+ local tags = {}
+ for k, t in ipairs(capi.screen[s]:tags()) do
+ if not tag.getproperty(t, "hide") and filter(t) then
+ table.insert(tags, t)
+ end
end
+
+ local function label(c) return taglist_label(c, style) end
+
+ common.list_update(w, buttons, label, data, template, tags)
+end
+
+--- Get the tag object the given widget appears on.
+-- @param widget The widget the look for.
+-- @return The tag object.
+function gettag(widget)
+ return common.tagwidgets[widget]
+end
+
+--- Create a new taglist widget.
+-- @param screen The screen to draw taglist for.
+-- @param filter Filter function to define what clients will be listed.
+-- @param buttons A table with buttons binding to set.
+-- @param style The style overrides default theme.
+-- bg_focus The background color for focused client.
+-- fg_focus The foreground color for focused client.
+-- bg_urgent The background color for urgent clients.
+-- fg_urgent The foreground color for urgent clients.
+-- squares_sel Optional: a user provided image for selected squares.
+-- squares_unsel Optional: a user provided image for unselected squares.
+-- squares_resize Optional: true or false to resize squares.
+-- font The font.
+-- @param template The template to use for each item.
+-- The default template:
+--
+--{
+-- {
+-- item = "icon"
+-- },
+-- {
+-- item = "title",
+-- bg_resize = true,
+-- },
+-- layout = layout.horizontal.leftright
+--}
+--
+function new(screen, filter, buttons, style, template)
+ local w = {
+ layout = layout.horizontal.leftright
+ }
+
+ template = template or {
+ {
+ item = "icon"
+ },
+ {
+ item = "title",
+ --margin = { left = 2, right = 2 },
+ bg_resize = true,
+ },
+ layout = layout.horizontal.leftright
+ }
+
+ local data = setmetatable({}, { __mode = 'k' })
+ local u = function (s)
+ if s == screen then
+ taglist_update(s, w, buttons, filter, data, style, template)
+ end
+ end
+ local uc = function (c) return u(c.screen) end
+ capi.client.add_signal("focus", uc)
+ capi.client.add_signal("unfocus", uc)
+ tag.attached_add_signal(screen, "property::selected", uc)
+ tag.attached_add_signal(screen, "property::icon", uc)
+ tag.attached_add_signal(screen, "property::hide", uc)
+ tag.attached_add_signal(screen, "property::name", uc)
+ capi.screen[screen]:add_signal("tag::attach", function(screen, tag)
+ u(screen.index)
+ end)
+ capi.screen[screen]:add_signal("tag::detach", function(screen, tag)
+ u(screen.index)
+ end)
+ capi.client.add_signal("new", function(c)
+ c:add_signal("property::urgent", uc)
+ c:add_signal("property::screen", function(c)
+ -- If client change screen, refresh it anyway since we don't from
+ -- which screen it was coming :-)
+ u(screen)
+ end)
+ c:add_signal("tagged", uc)
+ c:add_signal("untagged", uc)
+ end)
+ capi.client.add_signal("unmanage", uc)
+ u(screen)
+ return w
+end
+
+--- Filtering function to include all nonempty tags on the screen.
+-- @param t The tag.
+-- @param screen The screen we are drawing on.
+-- @return true if t is not empty, else false
+function filter.noempty(t, args)
+ if #t:clients() > 0 or t.selected then
+ return true
+ end
+
+ return false
+end
+
+--- Filtering function to include all tags on the screen.
+-- @param t The tag.
+-- @param screen The screen we are drawing on.
+-- @return true
+function filter.all(t, args)
+ return true
end
setmetatable(_M, { __call = function(_, ...) return new(...) end })
diff --git a/lib/awful/widget/tasklist.lua.in b/lib/awful/widget/tasklist.lua.in
index 448a38d0..66b69faa 100644
--- a/lib/awful/widget/tasklist.lua.in
+++ b/lib/awful/widget/tasklist.lua.in
@@ -23,64 +23,9 @@ local layout = require("awful.widget.layout")
module("awful.widget.tasklist")
-- Public structures
-label = {}
+filter = {}
-local function tasklist_update(w, buttons, label, data, widgets)
- local clients = capi.client.get()
- local shownclients = {}
- for k, c in ipairs(clients) do
- if not (c.skip_taskbar or c.hidden
- or c.type == "splash" or c.type == "dock" or c.type == "desktop") then
- table.insert(shownclients, c)
- end
- end
- clients = shownclients
-
- common.list_update(w, buttons, label, data, widgets, clients)
-end
-
---- Create a new tasklist widget.
--- @param label Label function to use.
--- @param buttons A table with buttons binding to set.
-function new(label, buttons)
- local w = {
- layout = layout.horizontal.flex
- }
- local widgets = { }
- widgets.imagebox = { }
- widgets.textbox = { margin = { left = 2,
- right = 2 },
- bg_resize = true,
- bg_align = "right"
- }
- local data = setmetatable({}, { __mode = 'k' })
- local u = function () tasklist_update(w, buttons, label, data, widgets) end
- for s = 1, capi.screen.count() do
- tag.attached_add_signal(s, "property::selected", u)
- capi.screen[s]:add_signal("tag::attach", u)
- capi.screen[s]:add_signal("tag::detach", u)
- end
- capi.client.add_signal("new", function (c)
- c:add_signal("property::urgent", u)
- c:add_signal("property::floating", u)
- c:add_signal("property::maximized_horizontal", u)
- c:add_signal("property::maximized_vertical", u)
- c:add_signal("property::name", u)
- c:add_signal("property::icon_name", u)
- c:add_signal("property::skip_taskbar", u)
- c:add_signal("property::hidden", u)
- c:add_signal("tagged", u)
- c:add_signal("untagged", u)
- end)
- capi.client.add_signal("unmanage", u)
- capi.client.add_signal("list", u)
- capi.client.add_signal("focus", u)
- capi.client.add_signal("unfocus", u)
- u()
- return w
-end
-
-local function widget_tasklist_label_common(c, args)
+local function tasklist_label(c, args)
if not args then args = {} end
local theme = beautiful.get()
local fg_focus = args.fg_focus or theme.tasklist_fg_focus or theme.fg_focus
@@ -123,86 +68,152 @@ local function widget_tasklist_label_common(c, args)
return text, bg, status_image, c.icon
end
---- Return labels for a tasklist widget with clients from all tags and screen.
--- It returns the client name and set a special
--- foreground and background color for focused client.
--- It also puts a special icon for floating windows.
--- @param c The client.
--- @param screen The screen we are drawing on.
--- @param args The arguments table.
--- bg_focus The background color for focused client.
--- fg_focus The foreground color for focused client.
--- bg_urgent The background color for urgent clients.
--- fg_urgent The foreground color for urgent clients.
--- @return A string to print, a background color and a status image.
-function label.allscreen(c, screen, args)
- return widget_tasklist_label_common(c, args)
+local function tasklist_update(s, w, buttons, filter, data, style, template)
+ local clients = {}
+ for k, c in ipairs(capi.client.get()) do
+ if not (c.skip_taskbar or c.hidden
+ or c.type == "splash" or c.type == "dock" or c.type == "desktop")
+ and filter(c, s) then
+ table.insert(clients, c)
+ end
+ end
+
+ local function label(c) return tasklist_label(c, style) end
+
+ common.list_update(w, buttons, label, data, template, clients)
end
---- Return labels for a tasklist widget with clients from all tags.
--- It returns the client name and set a special
--- foreground and background color for focused client.
--- It also puts a special icon for floating windows.
--- @param c The client.
--- @param screen The screen we are drawing on.
--- @param args The arguments table.
+--- Create a new tasklist widget.
+-- @param screen The screen to draw tasklist for.
+-- @param filter Filter function to define what clients will be listed.
+-- @param buttons A table with buttons binding to set.
+-- @param style The style overrides default theme.
-- bg_focus The background color for focused client.
-- fg_focus The foreground color for focused client.
-- bg_urgent The background color for urgent clients.
-- fg_urgent The foreground color for urgent clients.
--- @return A string to print, a background color and a status image.
-function label.alltags(c, screen, args)
- -- Only print client on the same screen as this widget
- if c.screen ~= screen then return end
- return widget_tasklist_label_common(c, args)
+-- bg_minimize The background color for minimized clients.
+-- fg_minimize The foreground color for minimized clients.
+-- floating_icon The icon for flating clients.
+-- font The font.
+-- @param template The template to use for each item.
+-- The default template:
+--
+-- {
+-- {
+-- item = "icon"
+-- },
+-- {
+-- {
+-- item = "title",
+-- margin = { left = 2, right = 2 },
+-- bg_resize = true,
+-- bg_align = "right"
+-- },
+-- layout = layout.horizontal.flex
+-- },
+-- layout = layout.horizontal.leftright
+-- }
+--
+function new(screen, filter, buttons, style, template)
+ local w = {
+ layout = layout.horizontal.flex
+ }
+ template = template or {
+ {
+ item = "icon"
+ },
+ {
+ {
+ item = "title",
+ margin = { left = 2, right = 2 },
+ bg_resize = true,
+ bg_align = "right"
+ },
+ layout = layout.horizontal.flex
+ },
+ layout = layout.horizontal.leftright
+ }
+
+ local data = setmetatable({}, { __mode = 'k' })
+ local u = function () tasklist_update(screen, w, buttons, filter, data, style, template) end
+ for s = 1, capi.screen.count() do
+ tag.attached_add_signal(s, "property::filtered", u)
+ capi.screen[s]:add_signal("tag::attach", u)
+ capi.screen[s]:add_signal("tag::detach", u)
+ capi.screen[s]:add_signal("tag::history::update", u)
+ end
+ capi.client.add_signal("new", function (c)
+ c:add_signal("property::urgent", u)
+ c:add_signal("property::floating", u)
+ c:add_signal("property::maximized_horizontal", u)
+ c:add_signal("property::maximized_vertical", u)
+ c:add_signal("property::name", u)
+ c:add_signal("property::icon_name", u)
+ c:add_signal("property::skip_taskbar", u)
+ c:add_signal("property::hidden", u)
+ c:add_signal("tagged", u)
+ c:add_signal("untagged", u)
+ end)
+ capi.client.add_signal("unmanage", u)
+ capi.client.add_signal("list", u)
+ capi.client.add_signal("focus", u)
+ capi.client.add_signal("unfocus", u)
+ u()
+ return w
end
---- Return labels for a tasklist widget with clients from currently selected tags.
--- It returns the client name and set a special
--- foreground and background color for focused client.
--- It also puts a special icon for floating windows.
+--- Filtering function to include all clients.
-- @param c The client.
-- @param screen The screen we are drawing on.
--- @param args The arguments table.
--- bg_focus The background color for focused client.
--- fg_focus The foreground color for focused client.
--- bg_urgent The background color for urgent clients.
--- fg_urgent The foreground color for urgent clients.
--- @return A string to print, a background color and a status image.
-function label.currenttags(c, screen, args)
+-- @return true
+function filter.allscreen(c, screen)
+ return true
+end
+
+--- Filtering function to include the clients from all tags on the screen.
+-- @param c The client.
+-- @param screen The screen we are drawing on.
+-- @return true if c is on screen, false otherwise
+function filter.alltags(c, screen)
-- Only print client on the same screen as this widget
- if c.screen ~= screen then return end
+ if c.screen ~= screen then return false end
+ return true
+end
+
+--- Filtering function to include only the clients from currently selected tags.
+-- @param c The client.
+-- @param screen The screen we are drawing on.
+-- @return true if c is in a selected tag on screen, false otherwise
+function filter.currenttags(c, screen)
+ -- Only print client on the same screen as this widget
+ if c.screen ~= screen then return false end
-- Include sticky client too
- if c.sticky then return widget_tasklist_label_common(c, args) end
- for k, t in ipairs(capi.screen[screen]:tags()) do
+ if c.sticky then return true end
+ tags = capi.screen[screen]:tags()
+ for k, t in ipairs(tags) do
if t.selected then
local ctags = c:tags()
for _, v in ipairs(ctags) do
if v == t then
- return widget_tasklist_label_common(c, args)
+ return true
end
end
end
end
+ return false
end
---- Return label for only the currently focused client.
--- It returns the client name and set a special
--- foreground and background color for focused client.
--- It also puts a special icon for floating windows.
+--- Filtering function to include only the currently focused client.
-- @param c The client.
-- @param screen The screen we are drawing on.
--- @param args The arguments table.
--- bg_focus The background color for focused client.
--- fg_focus The foreground color for focused client.
--- bg_urgent The background color for urgent clients.
--- fg_urgent The foreground color for urgent clients.
--- @return A string to print, a background color and a status image.
-function label.focused(c, screen, args)
+-- @return true if c is focused on screen, false otherwise
+function filter.focused(c, screen)
-- Only print client on the same screen as this widget
if c.screen == screen and capi.client.focus == c then
- return widget_tasklist_label_common(c, args)
+ return true
end
+ return false
end
setmetatable(_M, { __call = function(_, ...) return new(...) end })