widgets: use a geometry callback
[awesome.git] / lib / awful / widget.lua.in
blobff56722d0706846edf7688e7642b99b9c3c8b04b
1 ---------------------------------------------------------------------------
2 -- @author Julien Danjou <julien@danjou.info>
3 -- @copyright 2008 Julien Danjou
4 -- @release @AWESOME_VERSION@
5 ---------------------------------------------------------------------------
7 -- Grab environment we need
8 local ipairs = ipairs
9 local pairs = pairs
10 local table = table
11 local capi =
13 screen = screen,
14 client = client,
15 button = button,
16 widget = widget,
17 mouse = mouse
19 local util = require("awful.util")
20 local hooks = require("awful.hooks")
21 local beautiful = require("awful.beautiful")
22 local menu = require("awful.menu")
24 --- Widget module for awful
25 module("awful.widget")
27 -- Various public structures
28 taglist = {}
29 taglist.label = {}
30 tasklist = {}
31 tasklist.label = {}
33 --- Create a new taglist widget.
34 -- @param screen The screen to draw tag list.
35 -- @param label Label function to use.
36 -- @param buttons A table with buttons binding to set.
37 function taglist.new(scr, label, buttons)
38 local w = {}
39 local function taglist_update (screen)
40 -- Return right now if we do not care about this screen
41 if scr ~= screen then return end
42 local tags = capi.screen[screen]:tags()
43 -- Hack: if it has been registered as a widget in a wibox,
44 -- it's w.len since __len meta does not work on table until Lua 5.2.
45 -- Otherwise it's standard #w.
46 local len = w.len or #w
47 -- Add more widgets
48 if len < #tags then
49 for i = len + 1, #tags do
50 w[i] = capi.widget({ type = "textbox", name = "taglist" .. i })
51 end
52 -- Remove widgets
53 elseif len > #tags then
54 for i = #tags, len do
55 w[i] = nil
56 end
57 end
58 -- Update widgets text
59 for k, tag in ipairs(tags) do
60 w[k].text = label(tag)
61 if buttons then
62 -- Replace press function by a new one calling with tags as
63 -- argument.
64 -- This is done here because order of tags can change
65 local mbuttons = {}
66 for kb, b in ipairs(buttons) do
67 -- Copy object
68 mbuttons[kb] = capi.button(b)
69 mbuttons[kb].press = function () b.press(tag) end
70 end
71 w[k]:buttons(mbuttons)
72 end
73 end
74 end
75 hooks.arrange.register(taglist_update)
76 hooks.tags.register(taglist_update)
77 hooks.tagged.register(function (c, tag) taglist_update(c.screen) end)
78 hooks.property.register(function (c, prop)
79 if c.screen == scr and prop == "urgent" then
80 taglist_update(c.screen)
81 end
82 end)
83 taglist_update(scr)
84 return w
85 end
87 --- Return labels for a taglist widget with all tag from screen.
88 -- It returns the tag name and set a special
89 -- foreground and background color for selected tags.
90 -- @param t The tag.
91 -- @param args The arguments table.
92 -- bg_focus The background color for selected tag.
93 -- fg_focus The foreground color for selected tag.
94 -- bg_urgent The background color for urgent tags.
95 -- fg_urgent The foreground color for urgent tags.
96 -- taglist_squares Optional: set "true" or nil to display the taglist squares.
97 -- taglist_squares_sel Optional: an user provided image for selected squares.
98 -- taglist_squares_unsel Optional: an user provided image for unselected squares.
99 -- @return A string to print.
100 function taglist.label.all(t, args)
101 if not args then args = {} end
102 local theme = beautiful.get()
103 local fg_focus = args.fg_focus or theme.taglist_fg_focus or theme.fg_focus
104 local bg_focus = args.bg_focus or theme.taglist_bg_focus or theme.bg_focus
105 local fg_urgent = args.fg_urgent or theme.taglist_fg_urgent or theme.fg_urgent
106 local bg_urgent = args.bg_urgent or theme.taglist_bg_urgent or theme.bg_urgent
107 local taglist_squares = args.taglist_squares or theme.taglist_squares
108 local taglist_squares_sel = args.squares_sel or theme.squares_sel
109 local taglist_squares_unsel = args.squares_unsel or theme.squares_unsel
110 local text
111 local background = ""
112 local sel = capi.client.focus
113 local bg_color = nil
114 local fg_color = nil
115 if t.selected then
116 bg_color = bg_focus
117 fg_color = fg_focus
119 if sel and sel:tags()[t] then
120 if not taglist_squares or taglist_squares == "true" then
121 if taglist_squares_sel then
122 background = "resize=\"true\" image=\"" .. taglist_squares_sel .. "\""
123 else
124 background = "resize=\"true\" image=\"@AWESOME_ICON_PATH@/taglist/squarefw.png\""
127 elseif bg_urgent or fg_urgent then
128 for k, c in pairs(t:clients()) do
129 if not taglist_squares or taglist_squares == "true" then
130 if taglist_squares_unsel then
131 background = "resize=\"true\" image=\"" .. taglist_squares_unsel .. "\""
132 else
133 background = "resize=\"true\" image=\"@AWESOME_ICON_PATH@/taglist/squarew.png\""
136 if c.urgent then
137 bg_color = bg_urgent
138 fg_color = fg_urgent
139 break
143 if bg_color and fg_color then
144 text = "<bg "..background.." color='"..bg_color.."'/> <span color='"..util.color_strip_alpha(fg_color).."'>"..util.escape(t.name).."</span> "
145 else
146 text = " <bg "..background.." />"..util.escape(t.name).." "
148 return text
151 --- Return labels for a taglist widget with all *non empty* tags from screen.
152 -- It returns the tag name and set a special
153 -- foreground and background color for selected tags.
154 -- @param t The tag.
155 -- @param args The arguments table.
156 -- bg_focus The background color for selected tag.
157 -- fg_focus The foreground color for selected tag.
158 -- bg_urgent The background color for urgent tags.
159 -- fg_urgent The foreground color for urgent tags.
160 -- @return A string to print.
161 function taglist.label.noempty(t, args)
162 if #t:clients() > 0 or t.selected then
163 if not args then args = {} end
164 local theme = beautiful.get()
165 local fg_focus = args.fg_focus or theme.taglist_fg_focus or theme.fg_focus
166 local bg_focus = args.bg_focus or theme.taglist_bg_focus or theme.bg_focus
167 local fg_urgent = args.fg_urgent or theme.taglist_fg_urgent or theme.fg_urgent
168 local bg_urgent = args.bg_urgent or theme.taglist_bg_urgent or theme.bg_urgent
169 local bg_color = nil
170 local fg_color = nil
171 local text
173 if t.selected then
174 bg_color = bg_focus
175 fg_color = fg_focus
177 if bg_urgent and fg_urgent then
178 for k, c in pairs(t:clients()) do
179 if c.urgent then
180 bg_color = bg_urgent
181 fg_color = fg_urgent
182 break
186 if fg_color and bg_color then
187 text = "<bg color='" .. bg_color .. "'/> <span color='" .. util.color_strip_alpha(fg_color) .. "'>" .. util.escape(t.name) .. "</span> "
188 else
189 text = " " .. util.escape(t.name) .. " "
191 return text
195 --- Create a new tasklist widget.
196 -- @param label Label function to use.
197 -- @param buttons A table with buttons binding to set.
198 function tasklist.new(label, buttons)
199 local w = {}
200 local function tasklist_update ()
201 local clients = capi.client.get()
202 for k, c in ipairs(clients) do
203 if c.skip_taskbar or c.hide
204 or c.type == "splash" or c.type == "dock" or c.type == "desktop" then
205 table.remove(clients, k)
208 -- Hack: if it has been registered as a widget in a wibox,
209 -- it's w.len since __len meta does not work on table until Lua 5.2.
210 -- Otherwise it's standard #w.
211 local len = (w.len or #w) / 2
212 -- Add more widgets
213 if len < #clients then
214 for i = len * 2 + 1, #clients * 2, 2 do
215 w[i] = capi.widget({ type = "imagebox", name = "tasklist_icon" .. i, align = "flex" })
216 w[i + 1] = capi.widget({ type = "textbox", name = "tasklist_text" .. i, align = "flex" })
218 -- Remove widgets
219 elseif len > #clients then
220 for i = #clients * 2 + 1, len * 2, 2 do
221 w[i] = nil
222 w[i + 1] = nil
225 -- Update widgets text
226 for k = 1, #clients * 2, 2 do
227 if buttons then
228 -- Replace press function by a new one calling with tags as
229 -- argument
230 local mbuttons = {}
231 for kb, b in ipairs(buttons) do
232 -- Copy object
233 mbuttons[kb] = capi.button(b)
234 mbuttons[kb].press = function () b.press(clients[(k + 1) / 2]) end
236 w[k]:buttons(mbuttons)
237 w[k + 1]:buttons(mbuttons)
239 w[k + 1].text, w[k].bg = label(clients[(k + 1) / 2])
240 if w[k + 1].text then
241 -- Set icon
242 w[k].image = clients[(k + 1) / 2].icon
243 if w[k].image then
244 w[k].visible = true
245 else
246 w[k].visible = false
248 w[k + 1].visible = true
249 else
250 w[k].visible = false
251 w[k + 1].visible = false
255 hooks.clients.register(tasklist_update)
256 hooks.tagged.register(tasklist_update)
257 hooks.focus.register(tasklist_update)
258 hooks.unfocus.register(tasklist_update)
259 hooks.property.register(function (c, prop)
260 if prop == "urgent"
261 or prop == "floating"
262 or prop == "icon"
263 or prop == "name"
264 or prop == "icon_name" then
265 tasklist_update()
267 end)
268 tasklist_update()
269 return w
272 local function widget_tasklist_label_common(c, args)
273 if not args then args = {} end
274 local theme = beautiful.get()
275 local fg_focus = args.fg_focus or theme.tasklist_fg_focus or theme.fg_focus
276 local bg_focus = args.bg_focus or theme.tasklist_bg_focus or theme.bg_focus
277 local fg_urgent = args.fg_urgent or theme.tasklist_fg_urgent or theme.fg_urgent
278 local bg_urgent = args.bg_urgent or theme.tasklist_bg_urgent or theme.bg_urgent
279 local bg = nil
280 local text = "<margin left=\"2\" right=\"2\"/>"
281 local name
282 if c.floating then
283 text = text.."<bg image=\"@AWESOME_ICON_PATH@/tasklist/floatingw.png\" align=\"right\"/>"
285 if c.minimized then
286 name = util.escape(c.icon_name) or ""
287 else
288 name = util.escape(c.name) or ""
290 if capi.client.focus == c then
291 if bg_focus and fg_focus then
292 bg = bg_focus
293 text = text .. "<bg color='"..bg_focus.."'/><span color='"..util.color_strip_alpha(fg_focus).."'>"..name.."</span>"
294 else
295 text = text .. name
297 elseif c.urgent and bg_urgent and fg_urgent then
298 local bg = bg_urgent
299 text = text .. "<bg color='"..bg_urgent.."'/><span color='"..util.color_strip_alpha(fg_urgent).."'>"..name.."</span>"
300 else
301 text = text .. name
303 return text, bg
306 --- Return labels for a tasklist widget with clients from all tags and screen.
307 -- It returns the client name and set a special
308 -- foreground and background color for focused client.
309 -- It also puts a special icon for floating windows.
310 -- @param c The client.
311 -- @param screen The screen we are drawing on.
312 -- @param args The arguments table.
313 -- bg_focus The background color for focused client.
314 -- fg_focus The foreground color for focused client.
315 -- bg_urgent The background color for urgent clients.
316 -- fg_urgent The foreground color for urgent clients.
317 -- @return A string to print.
318 function tasklist.label.allscreen(c, screen, args)
319 return widget_tasklist_label_common(c, args)
322 --- Return labels for a tasklist widget with clients from all tags.
323 -- It returns the client name and set a special
324 -- foreground and background color for focused client.
325 -- It also puts a special icon for floating windows.
326 -- @param c The client.
327 -- @param screen The screen we are drawing on.
328 -- @param args The arguments table.
329 -- bg_focus The background color for focused client.
330 -- fg_focus The foreground color for focused client.
331 -- bg_urgent The background color for urgent clients.
332 -- fg_urgent The foreground color for urgent clients.
333 -- @return A string to print.
334 function tasklist.label.alltags(c, screen, args)
335 -- Only print client on the same screen as this widget
336 if c.screen ~= screen then return end
337 return widget_tasklist_label_common(c, args)
340 --- Return labels for a tasklist widget with clients from currently selected tags.
341 -- It returns the client name and set a special
342 -- foreground and background color for focused client.
343 -- It also puts a special icon for floating windows.
344 -- @param c The client.
345 -- @param screen The screen we are drawing on.
346 -- @param args The arguments table.
347 -- bg_focus The background color for focused client.
348 -- fg_focus The foreground color for focused client.
349 -- bg_urgent The background color for urgent clients.
350 -- fg_urgent The foreground color for urgent clients.
351 -- @return A string to print.
352 function tasklist.label.currenttags(c, screen, args)
353 -- Only print client on the same screen as this widget
354 if c.screen ~= screen then return end
355 for k, t in ipairs(capi.screen[screen]:tags()) do
356 if t.selected and c:tags()[t] then
357 return widget_tasklist_label_common(c, args)
362 --- Create a button widget. When clicked, the image is deplaced to make it like
363 -- a real button.
364 -- @param args Standard widget table arguments, plus image for the image path.
365 -- @return A textbox widget configured as a button.
366 function button(args)
367 if not args then return end
368 args.type = "textbox"
369 local w = capi.widget(args)
370 local img_release = "<bg image=\"" .. args.image .. "\" resize=\"true\"/>"
371 local img_press = "<bg_margin top=\"2\" left=\"2\"/><bg image=\"" .. args.image .. "\" resize=\"true\"/>"
372 w.text = img_release
373 w:buttons({ capi.button({}, 1, function () w.text = img_press end, function () w.text = img_release end) })
374 function w.mouse_leave(s) w.text = img_release end
375 function w.mouse_enter(s) if capi.mouse.coords().buttons[1] then w.text = img_press end end
376 return w
379 --- Create a button widget which will launch a command.
380 -- @param args Standard widget table arguments, plus image for the image path
381 -- and command for the command to run on click, or either menu to create menu.
382 -- @return A launcher widget.
383 function launcher(args)
384 if not args.command and not args.menu then return end
385 local w = button(args)
386 local b = w:buttons()
388 if args.command then
389 b[#b + 1] = capi.button({}, 1, nil, function () util.spawn(args.command) end)
390 elseif args.menu then
391 b[#b + 1] = capi.button({}, 1, nil, function () menu.new(args.menu[1], args.menu[2]) end)
394 w:buttons(b)
395 return w
398 -- vim: filetype=lua:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:encoding=utf-8:textwidth=80