common: Refactor awful.widget.common to support external extensions

This commit allow user defined delegates to be used as list elements.
This put an end to the endless attempt to cram more features into this
code.

A widget template (non-instantiated) is passed to the arguments and
is created by the common code. It also supports "roles" where some
user defined widgets can replace the old textbox or imagebox.
This commit is contained in:
Emmanuel Lepage Vallee 2017-08-10 00:28:06 -04:00
parent 2e7cd2b2ef
commit f147f0d28a
1 changed files with 94 additions and 50 deletions

View File

@ -9,7 +9,9 @@ local type = type
local ipairs = ipairs
local capi = { button = button }
local wibox = require("wibox")
local gdebug = require("gears.debug")
local dpi = require("beautiful").xresources.apply_dpi
local base = require("wibox.widget.base")
--- Common utilities for awful widgets
local common = {}
@ -36,33 +38,13 @@ function common.create_buttons(buttons, object)
end
end
--- Common update method.
-- @param w The widget.
-- @tab buttons
-- @func label Function to generate label parameters from an object.
-- The function gets passed an object from `objects`, and
-- has to return `text`, `bg`, `bg_image`, `icon`.
-- @tab data Current data/cache, indexed by objects.
-- @tab objects Objects to be displayed / updated.
function common.list_update(w, buttons, label, data, objects)
-- update the widgets, creating them if needed
w:reset()
for i, o in ipairs(objects) do
local cache = data[o]
local ib, tb, bgb, tbm, ibm, l
if cache then
ib = cache.ib
tb = cache.tb
bgb = cache.bgb
tbm = cache.tbm
ibm = cache.ibm
else
ib = wibox.widget.imagebox()
tb = wibox.widget.textbox()
bgb = wibox.container.background()
tbm = wibox.container.margin(tb, dpi(4), dpi(4))
ibm = wibox.container.margin(ib, dpi(4))
l = wibox.layout.fixed.horizontal()
local function default_template()
local ib = wibox.widget.imagebox()
local tb = wibox.widget.textbox()
local bgb = wibox.container.background()
local tbm = wibox.container.margin(tb, dpi(4), dpi(4))
local ibm = wibox.container.margin(ib, dpi(4))
local l = wibox.layout.fixed.horizontal()
-- All of this is added in a fixed widget
l:fill_space(true)
@ -72,45 +54,107 @@ function common.list_update(w, buttons, label, data, objects)
-- And all of this gets a background
bgb:set_widget(l)
bgb:buttons(common.create_buttons(buttons, o))
data[o] = {
return {
ib = ib,
tb = tb,
bgb = bgb,
tbm = tbm,
ibm = ibm,
primary = l,
}
end
local function custom_template(args)
local l = base.make_widget_from_value(args.widget_template)
-- The template system requires being able to get children elements by ids.
-- This is not optimal, but for now there is no way around it.
assert(l.get_children_by_id,"The given widget template did not result in a"..
"layout with a 'get_children_by_id' method")
return {
ib = l:get_children_by_id( "icon_role" )[1],
tb = l:get_children_by_id( "text_role" )[1],
bgb = l:get_children_by_id( "background_role" )[1],
tbm = l:get_children_by_id( "text_margin_role" )[1],
ibm = l:get_children_by_id( "icon_margin_role" )[1],
primary = l,
update_callback = l.update_callback,
create_callback = l.create_callback,
}
end
--- Common update method.
-- @param w The widget.
-- @tab buttons
-- @func label Function to generate label parameters from an object.
-- The function gets passed an object from `objects`, and
-- has to return `text`, `bg`, `bg_image`, `icon`.
-- @tab data Current data/cache, indexed by objects.
-- @tab objects Objects to be displayed / updated.
-- @tparam[opt={}] table args
function common.list_update(w, buttons, label, data, objects, args)
-- update the widgets, creating them if needed
w:reset()
for i, o in ipairs(objects) do
local cache = data[o]
if not cache then
cache = (args and args.widget_template) and
custom_template(args) or default_template()
cache.primary:buttons(common.create_buttons(buttons, o))
if cache.create_callback then
cache.create_callback(cache.primary, o, i, objects)
end
local text, bg, bg_image, icon, args = label(o, tb)
args = args or {}
data[o] = cache
elseif cache.update_callback then
cache.update_callback(cache.primary, o, i, objects)
end
local text, bg, bg_image, icon, item_args = label(o, cache.tb)
item_args = item_args or {}
-- The text might be invalid, so use pcall.
if text == nil or text == "" then
tbm:set_margins(0)
else
if not tb:set_markup_silently(text) then
tb:set_markup("<i>&lt;Invalid text&gt;</i>")
if cache.tbm and (text == nil or text == "") then
cache.tbm:set_margins(0)
elseif cache.tb then
if not cache.tb:set_markup_silently(text) then
cache.tb:set_markup("<i>&lt;Invalid text&gt;</i>")
end
end
bgb:set_bg(bg)
if type(bg_image) == "function" then
-- TODO: Why does this pass nil as an argument?
bg_image = bg_image(tb,o,nil,objects,i)
end
bgb:set_bgimage(bg_image)
if icon then
ib:set_image(icon)
else
ibm:set_margins(0)
end
bgb.shape = args.shape
bgb.shape_border_width = args.shape_border_width
bgb.shape_border_color = args.shape_border_color
if cache.bgb then
cache.bgb:set_bg(bg)
w:add(bgb)
--TODO v5 remove this if, it existed only for a removed and
-- undocumented API
if type(bg_image) ~= "function" then
cache.bgb:set_bgimage(bg_image)
else
gdebug.deprecate("If you read this, you used an undocumented API"..
" which has been replaced by the new awful.widget.common "..
"templating system, please migrate now. This feature is "..
"already staged for removal", {
deprecated_in = 4
})
end
cache.bgb.shape = item_args.shape
cache.bgb.shape_border_width = item_args.shape_border_width
cache.bgb.shape_border_color = item_args.shape_border_color
end
if cache.ib and icon then
cache.ib:set_image(icon)
elseif cache.ibm then
cache.ibm:set_margins(0)
end
w:add(cache.primary)
end
end