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 })