awesome/lib/awful/widget.lua.in

393 lines
14 KiB
Lua

---------------------------------------------------------------------------
-- @author Julien Danjou <julien@danjou.info>
-- @copyright 2008 Julien Danjou
-- @release @AWESOME_VERSION@
---------------------------------------------------------------------------
-- Grab environment we need
local ipairs = ipairs
local pairs = pairs
local table = table
local otable = otable
local capi =
{
screen = screen,
client = client,
button = button,
widget = widget,
mouse = mouse
}
local util = require("awful.util")
local hooks = require("awful.hooks")
local beautiful = require("beautiful")
local menu = require("awful.menu")
--- Widget module for awful
module("awful.widget")
-- Various public structures
taglist = {}
taglist.label = {}
tasklist = {}
tasklist.label = {}
local function taglist_update (screen, w, label, buttons, data)
local tags = capi.screen[screen]:tags()
-- 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
-- Add more widgets
if len < #tags then
for i = len + 1, #tags do
w[i] = capi.widget({ type = "textbox", name = "taglist" .. i })
end
-- Remove widgets
elseif len > #tags then
for i = #tags + 1, len do
w[i] = nil
end
end
-- Update widgets text
for k, tag in ipairs(tags) do
w[k].text = label(tag)
if buttons then
if not data[tag] then
-- Replace press function by a new one calling with tags as
-- argument.
-- This is done here because order of tags can change
data[tag] = {}
for kb, b in ipairs(buttons) do
-- Copy object
data[tag][kb] = capi.button(b)
data[tag][kb].press = function () b.press(tag) end
end
end
w[k]:buttons(data[tag])
end
end
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 taglist.new(screen, label, buttons)
local w = {}
local data = otable()
local u = function (s)
if s == screen then
taglist_update(s, w, label, buttons, data)
end
end
local uc = function (c) return u(c.screen) end
hooks.focus.register(uc)
hooks.unfocus.register(uc)
hooks.arrange.register(u)
hooks.tags.register(u)
hooks.tagged.register(uc)
hooks.property.register(function (c, prop)
if prop == "urgent" then
u(c.screen)
end
end)
-- Free data on tag removal
hooks.tags.register(function (s, tag, action)
if action == "remove" then data[tag] = nil end
end)
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.
function taglist.label.all(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
local bg_focus = args.bg_focus or theme.taglist_bg_focus or theme.bg_focus
local fg_urgent = args.fg_urgent or theme.taglist_fg_urgent or theme.fg_urgent
local bg_urgent = args.bg_urgent or theme.taglist_bg_urgent or theme.bg_urgent
local taglist_squares_sel = args.squares_sel or theme.taglist_squares_sel
local taglist_squares_unsel = args.squares_unsel or theme.taglist_squares_unsel
local taglist_squares_resize = theme.taglist_squares_resize or args.squares_resize or "true"
local text
local background = ""
local sel = capi.client.focus
local bg_color = nil
local fg_color = nil
if t.selected then
bg_color = bg_focus
fg_color = fg_focus
end
if sel and sel:tags()[t] then
if taglist_squares_sel then
background = "resize=\"" .. taglist_squares_resize .. "\" image=\"" .. taglist_squares_sel .. "\""
end
else
local cls = t:clients()
if #cls > 0 and taglist_squares_unsel then
background = "resize=\"" .. taglist_squares_resize .. "\" image=\"" .. taglist_squares_unsel .. "\""
end
for k, c in pairs(cls) do
if c.urgent then
if bg_urgent then bg_color = bg_urgent end
if fg_urgent then fg_color = fg_urgent end
break
end
end
end
if bg_color and fg_color then
text = "<bg "..background.." color='"..bg_color.."'/> <span color='"..util.color_strip_alpha(fg_color).."'>"..util.escape(t.name).."</span> "
else
text = " <bg "..background.." />"..util.escape(t.name).." "
end
return text
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.
function taglist.label.noempty(t, args)
if #t:clients() > 0 or t.selected then
return taglist.label.all(t, args)
end
end
local function tasklist_update(w, buttons, label, data)
local clients = capi.client.get()
local shownclients = {}
for k, c in ipairs(clients) do
if not (c.skip_taskbar or c.hide
or c.type == "splash" or c.type == "dock" or c.type == "desktop") then
table.insert(shownclients, c)
end
end
clients = shownclients
-- 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 < #clients then
for i = len * 2 + 1, #clients * 2, 2 do
w[i] = capi.widget({ type = "imagebox", name = "tasklist_icon" .. i, align = "flex" })
w[i + 1] = capi.widget({ type = "textbox", name = "tasklist_text" .. i, align = "flex" })
end
-- Remove widgets
elseif len > #clients then
for i = #clients * 2 + 1, len * 2, 2 do
w[i] = nil
w[i + 1] = nil
end
end
-- Update widgets text
for k = 1, #clients * 2, 2 do
if buttons then
local c = clients[(k + 1) / 2]
if not data[c] then
data[c] = {}
-- Replace press function by a new one calling with tags as
-- argument
for kb, b in ipairs(buttons) do
-- Copy object
data[c][kb] = capi.button(b)
data[c][kb].press = function () b.press(c) end
end
end
w[k]:buttons(data[c])
w[k + 1]:buttons(data[c])
end
w[k + 1].text, w[k].bg = label(clients[(k + 1) / 2])
if w[k + 1].text then
-- Set icon
w[k].image = clients[(k + 1) / 2].icon
if w[k].image then
w[k].visible = true
else
w[k].visible = false
end
w[k + 1].visible = true
else
w[k].visible = false
w[k + 1].visible = false
end
end
end
--- Create a new tasklist widget.
-- @param label Label function to use.
-- @param buttons A table with buttons binding to set.
function tasklist.new(label, buttons)
local w = {}
local data = otable()
local u = function () tasklist_update(w, buttons, label, data) end
hooks.arrange.register(u)
hooks.clients.register(u)
hooks.tagged.register(u)
hooks.focus.register(u)
hooks.unfocus.register(u)
hooks.property.register(function (c, prop)
if prop == "urgent"
or prop == "floating"
or prop == "icon"
or prop == "name"
or prop == "icon_name" then
u()
end
end)
u()
-- Free data on unmanage
hooks.unmanage.register(function (c) data[c] = nil end)
return w
end
local function widget_tasklist_label_common(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
local bg_focus = args.bg_focus or theme.tasklist_bg_focus or theme.bg_focus
local fg_urgent = args.fg_urgent or theme.tasklist_fg_urgent or theme.fg_urgent
local bg_urgent = args.bg_urgent or theme.tasklist_bg_urgent or theme.bg_urgent
local floating_icon = args.floating_icon or theme.tasklist_floating_icon
local floating_icon_align = args.floating_icon_align or theme.tasklist_floating_icon_align or "right"
local bg = nil
local text = "<margin left=\"2\" right=\"2\"/>"
local name
if c.floating and floating_icon then
text = text.."<bg image=\"" .. floating_icon .. "\" align=\"" .. floating_icon_align .. "\"/>"
end
if c.minimize then
name = util.escape(c.icon_name) or ""
else
name = util.escape(c.name) or ""
end
if capi.client.focus == c then
if bg_focus and fg_focus then
bg = bg_focus
text = text .. "<bg color='"..bg_focus.."'/><span color='"..util.color_strip_alpha(fg_focus).."'>"..name.."</span>"
else
text = text .. name
end
elseif c.urgent and bg_urgent and fg_urgent then
bg = bg_urgent
text = text .. "<bg color='"..bg_urgent.."'/><span color='"..util.color_strip_alpha(fg_urgent).."'>"..name.."</span>"
else
text = text .. name
end
return text, bg
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.
function tasklist.label.allscreen(c, screen, args)
return widget_tasklist_label_common(c, args)
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.
-- 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.
function tasklist.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)
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.
-- @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.
function tasklist.label.currenttags(c, screen, args)
-- Only print client on the same screen as this widget
if c.screen ~= screen then return end
for k, t in ipairs(capi.screen[screen]:tags()) do
if t.selected and c:tags()[t] then
return widget_tasklist_label_common(c, args)
end
end
end
--- Create a button widget. When clicked, the image is deplaced to make it like
-- a real button.
-- @param args Standard widget table arguments, plus image for the image path.
-- @return A textbox widget configured as a button.
function button(args)
if not args or not args.image then return end
args.type = "textbox"
local w = capi.widget(args)
local img_release = "<bg image=\"" .. args.image .. "\" resize=\"true\"/>"
local img_press = "<bg_margin top=\"2\" left=\"2\"/><bg image=\"" .. args.image .. "\" resize=\"true\"/>"
w.text = img_release
w:buttons({ capi.button({}, 1, function () w.text = img_press end, function () w.text = img_release end) })
function w.mouse_leave(s) w.text = img_release end
function w.mouse_enter(s) if capi.mouse.coords().buttons[1] then w.text = img_press end end
return w
end
--- Create a button widget which will launch a command.
-- @param args Standard widget table arguments, plus image for the image path
-- and command for the command to run on click, or either menu to create menu.
-- @return A launcher widget.
function launcher(args)
if not args.command and not args.menu then return end
local w = button(args)
if not w then return end
local b = w:buttons()
if args.command then
b[#b + 1] = capi.button({}, 1, nil, function () util.spawn(args.command) end)
elseif args.menu then
b[#b + 1] = capi.button({}, 1, nil, function () args.menu:toggle() end)
end
w:buttons(b)
return w
end
-- vim: filetype=lua:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:encoding=utf-8:textwidth=80