awful.titlebar: add signals for various properties (FS#609)
[awesome.git] / lib / awful / titlebar.lua.in
blob5c1d88ee140dcb82af14307290afdcdcfcaafad4
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 math = math
9 local image = image
10 local pairs = pairs
11 local type = type
12 local setmetatable = setmetatable
13 local type = type
14 local capi =
16 awesome = awesome,
17 wibox = wibox,
18 widget = widget,
19 client = client,
21 local abutton = require("awful.button")
22 local beautiful = require("beautiful")
23 local util = require("awful.util")
24 local widget = require("awful.widget")
25 local mouse = require("awful.mouse")
26 local client = require("awful.client")
27 local layout = require("awful.widget.layout")
29 --- Titlebar module for awful
30 module("awful.titlebar")
32 -- Privata data
33 local data = setmetatable({}, { __mode = 'k' })
35 -- Predeclaration for buttons
36 local button_groups
38 local function button_callback_focus_raise_move(w, t)
39 capi.client.focus = t.client
40 t.client:raise()
41 mouse.client.move(t.client)
42 end
44 local function button_callback_move(w, t)
45 return mouse.client.move(t.client)
46 end
48 local function button_callback_resize(w, t)
49 return mouse.client.resize(t.client)
50 end
52 --- Create a standard titlebar.
53 -- @param c The client.
54 -- @param args Arguments.
55 -- modkey: the modkey used for the bindings.
56 -- fg: the foreground color.
57 -- bg: the background color.
58 -- fg_focus: the foreground color for focused window.
59 -- fg_focus: the background color for focused window.
60 -- width: the titlebar width
61 function add(c, args)
62 if not c or (c.type ~= "normal" and c.type ~= "dialog") then return end
63 if not args then args = {} end
64 if not args.height then args.height = capi.awesome.font_height * 1.5 end
65 local theme = beautiful.get()
66 -- Store colors
67 data[c] = {}
68 data[c].fg = args.fg or theme.titlebar_fg_normal or theme.fg_normal
69 data[c].bg = args.bg or theme.titlebar_bg_normal or theme.bg_normal
70 data[c].fg_focus = args.fg_focus or theme.titlebar_fg_focus or theme.fg_focus
71 data[c].bg_focus = args.bg_focus or theme.titlebar_bg_focus or theme.bg_focus
72 data[c].width = args.width
73 data[c].font = args.font or theme.titlebar_font or theme.font
75 local tb = capi.wibox(args)
77 local title = capi.widget({ type = "textbox" })
78 if c.name then
79 title.text = "<span font_desc='" .. data[c].font .. "'> " ..
80 util.escape(c.name) .. " </span>"
81 end
83 -- Redirect relevant events to the client the titlebar belongs to
84 local bts = util.table.join(
85 abutton({ }, 1, button_callback_focus_raise_move),
86 abutton({ args.modkey }, 1, button_callback_move),
87 abutton({ args.modkey }, 3, button_callback_resize))
88 title:buttons(bts)
90 local appicon = capi.widget({ type = "imagebox" })
91 appicon.image = c.icon
93 -- for each button group, call create for the client.
94 -- if a button set is created add the set to the
95 -- data[c].button_sets for late updates and add the
96 -- individual buttons to the array part of the widget
97 -- list
98 local widget_list = {
99 layout = layout.horizontal.rightleft
101 local iw = 1
102 local is = 1
103 data[c].button_sets = {}
104 for i = 1, #button_groups do
105 local set = button_groups[i].create(c, args.modkey, theme)
106 if (set) then
107 data[c].button_sets[is] = set
108 is = is + 1
109 for n,b in pairs(set) do
110 widget_list[iw] = b
111 iw = iw + 1
116 tb.widgets = {
117 widget_list,
119 appicon = appicon,
120 title = title,
121 layout = layout.horizontal.flex
123 layout = layout.horizontal.rightleft
126 c.titlebar = tb
128 c:add_signal("property::icon", update)
129 c:add_signal("property::name", update)
130 c:add_signal("property::sticky", update)
131 c:add_signal("property::floating", update)
132 c:add_signal("property::ontop", update)
133 c:add_signal("property::maximized_vertical", update)
134 c:add_signal("property::maximized_horizontal", update)
135 update(c)
138 --- Update a titlebar. This should be called in some hooks.
139 -- @param c The client to update.
140 -- @param prop The property name which has changed.
141 function update(c)
142 if c.titlebar and data[c] then
143 local widgets = c.titlebar.widgets
144 if widgets[2].title then
145 widgets[2].title.text = "<span font_desc='" .. data[c].font ..
146 "'> ".. util.escape(c.name) .. " </span>"
148 if widgets[2].appicon then
149 widgets[2].appicon.image = c.icon
151 if capi.client.focus == c then
152 c.titlebar.fg = data[c].fg_focus
153 c.titlebar.bg = data[c].bg_focus
154 else
155 c.titlebar.fg = data[c].fg
156 c.titlebar.bg = data[c].bg
159 -- iterated of all registered button_sets and update
160 local sets = data[c].button_sets
161 for i = 1, #sets do
162 sets[i].update(c,prop)
167 --- Remove a titlebar from a client.
168 -- @param c The client.
169 function remove(c)
170 c.titlebar = nil
171 data[c] = nil
174 -- Create a new button for the toolbar
175 -- @param c The client of the titlebar
176 -- @param name The base name of the button (i.e. close)
177 -- @param modkey ... you know that one, don't you?
178 -- @param theme The theme from beautifull. Used to get the image paths
179 -- @param state The state the button is associated to. Containse path the action and info about the image
180 local function button_new(c, name, modkey, theme, state)
181 local bts = abutton({ }, 1, nil, state.action)
183 -- get the image path from the theme. Only return a button if we find an image
184 local img
185 img = "titlebar_" .. name .. "_button_" .. state.img
186 img = theme[img]
187 if not img then return end
188 img = image(img)
189 if not img then return end
191 -- now create the button
192 local bname = name .. "_" .. state.idx
193 local button = widget.button({ image = img })
194 if not button then return end
195 local rbts = button:buttons()
197 for k, v in pairs(rbts) do
198 bts[#bts + 1] = v
201 button:buttons(bts)
202 button.visible = false
203 return button
206 -- Update the buttons in a button group
207 -- @param s The button group to update
208 -- @param c The client of the titlebar
209 -- @param p The property that has changed
210 local function button_group_update(s,c,p)
211 -- hide the currently active button, get the new state and show the new button
212 local n = s.select_state(c,p)
213 if n == nil then return end
214 if (s.active ~= nil) then s.active.visible = false end
215 s.active = s.buttons[n]
216 s.active.visible = true
219 -- Create all buttons in a group
220 -- @param c The client of the titlebar
221 -- @param group The button group to create the buttons for
222 -- @param modkey ...
223 -- @param theme Theme for the image paths
224 local function button_group_create(c, group, modkey, theme )
225 local s = {}
226 s.name = group.name
227 s.select_state = group.select_state
228 s.buttons = {
229 layout = layout.horizontal.rightleft
231 for n,state in pairs(group.states) do
232 s.buttons[n] = button_new(c, s.name, modkey, theme, state)
233 if (s.buttons[n] == nil) then return end
234 for a,v in pairs(group.attributes) do
235 s.buttons[n][a] = v
238 function s.update(c,p) button_group_update(s,c,p) end
239 return s
242 -- Builds a new button group
243 -- @param name The base name for the buttons in the group (i.e. "close")
244 -- @param attrs Common attributes for the buttons (i.e. {align = "right")
245 -- @param sfn State select function.
246 -- @param args The states of the button
247 local function button_group(name, attrs, sfn, ...)
248 local s = {}
249 s.name = name
250 s.select_state = sfn
251 s.attributes = attrs
252 s.states = {}
254 for i = 1, #arg do
255 local state = arg[i]
256 s.states[state.idx] = state
259 function s.create(c,modkey, theme) return button_group_create(c,s,modkey, theme) end
260 return s
263 -- Select a state for a client based on an attribute of the client and whether it has focus
264 -- @param c The client of the titlebar
265 -- @param p The property that has changed
266 -- @param a The property to check
267 local function select_state(c,p,a)
268 if (c == nil) then return "n/i" end
269 if capi.client.focus == c then
270 if c[a] then
271 return "f/a"
272 else
273 return "f/i"
275 else
276 if c[a] then
277 return "n/a"
278 else
279 return "n/i"
284 -- Select a state for a client based on whether it's floating or not
285 -- @param c The client of the titlebar
286 -- @param p The property that has changed
287 local function select_state_floating(c,p)
288 if not c then return end
289 if capi.client.focus == c then
290 if client.floating.get(c) then
291 return "f/a"
293 return "f/i"
295 if client.floating.get(c) then
296 return "n/a"
298 return "n/i"
301 -- Select a state for a client based on whether it's maximized or not
302 -- @param c The client of the titlebar
303 -- @param p The property that has changed
304 local function select_state_maximized(c,p)
305 if (c == nil) then return "n/i" end
306 if capi.client.focus == c then
307 if c.maximized_horizontal or c.maximized_vertical then
308 return "f/a"
309 else
310 return "f/i"
312 else
313 if c.maximized_horizontal or c.maximized_vertical then
314 return "n/a"
315 else
316 return "n/i"
321 -- Select a state for a client based on whether it has focus or not
322 -- @param c The client of the titlebar
323 -- @param p The property that has changed
324 local function select_state_focus(c,p)
325 if c and capi.client.focus == c then
326 return "f"
328 return "n"
331 -- These are the predefined button groups
332 -- A short explanation using 'close_buttons' as an example:
333 -- "close" : name of the button, the images for this button are taken from the
334 -- theme variables titlebar_close_button_...
335 -- { align ... : attributes of all the buttons
336 -- select_state_focus : This function returns a short string used to describe
337 -- the state. In this case either "n" or "f" depending on
338 -- the focus state of the client. These strings can be
339 -- choosen freely but the< must match one of the idx fuekds
340 -- of the states below
341 -- { idx = "n" ... : This is the state of the button for the 'unfocussed'
342 -- (normal) state. The idx = "n" parameter connects this
343 -- button to the return value of the 'select_state_focus'
344 -- function. The img = "normal" parameter is used to
345 -- determine its image. In this case the iamge is taken from
346 -- the theme variable "titlebar_close_button_normal".
347 -- Finally the last parameter is the action for mouse
348 -- button 1
350 local ontop_buttons = button_group("ontop",
351 { align = "right" },
352 function(c,p) return select_state(c, p, "ontop") end,
353 { idx = "n/i", img = "normal_inactive",
354 action = function(w, t) t.client.ontop = true end },
355 { idx = "f/i", img = "focus_inactive",
356 action = function(w, t) t.client.ontop = true end },
357 { idx = "n/a", img = "normal_active",
358 action = function(w, t) t.client.ontop = false end },
359 { idx = "f/a", img = "focus_active",
360 action = function(w, t) t.client.ontop = false end })
362 local sticky_buttons = button_group("sticky",
363 { align = "right" },
364 function(c,p) return select_state(c,p,"sticky") end,
365 { idx = "n/i", img = "normal_inactive",
366 action = function(w, t) t.client.sticky = true end },
367 { idx = "f/i", img = "focus_inactive",
368 action = function(w, t) t.client.sticky = true end },
369 { idx = "n/a", img = "normal_active",
370 action = function(w, t) t.client.sticky = false end },
371 { idx = "f/a", img = "focus_active",
372 action = function(w, t) t.client.sticky = false end })
374 local maximized_buttons = button_group("maximized",
375 { align = "right" },
376 select_state_maximized,
377 { idx = "n/i", img = "normal_inactive",
378 action = function(w, t) t.client.maximized_horizontal = true
379 t.client.maximized_vertical = true end },
380 { idx = "f/i", img = "focus_inactive",
381 action = function(w, t) t.client.maximized_horizontal = true
382 t.client.maximized_vertical = true end },
383 { idx = "n/a", img = "normal_active",
384 action = function(w, t) t.client.maximized_horizontal = false
385 t.client.maximized_vertical = false end },
386 { idx = "f/a", img = "focus_active",
387 action = function(w, t) t.client.maximized_horizontal = false
388 t.client.maximized_vertical = false end })
390 local close_buttons = button_group("close",
391 { align = "left" },
392 select_state_focus,
393 { idx = "n", img = "normal",
394 action = function (w, t) t.client:kill() end },
395 { idx = "f", img = "focus",
396 action = function (w, t) t.client:kill() end })
398 local function floating_update(w, t)
399 client.floating.toggle(t.client)
402 local floating_buttons = button_group("floating",
403 { align = "right"},
404 select_state_floating,
405 { idx = "n/i", img = "normal_inactive", action = floating_update },
406 { idx = "f/i", img = "focus_inactive", action = floating_update },
407 { idx = "n/a", img = "normal_active", action = floating_update },
408 { idx = "f/a", img = "focus_active", action = floating_update })
410 button_groups = { close_buttons,
411 ontop_buttons,
412 sticky_buttons,
413 maximized_buttons,
414 floating_buttons }
416 -- Register standards hooks
417 capi.client.add_signal("focus", update)
418 capi.client.add_signal("unfocus", update)
420 -- vim: filetype=lua:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:encoding=utf-8:textwidth=80