Remove "encoding=utf-8" from Vim modelines
[awesome.git] / lib / awful / titlebar.lua.in
blob1bda9dad1438fe228b2d38e4d278034be942a612
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 if not args.widget then customwidget = {} else customwidget = args.widget end
67 -- Store colors
68 data[c] = {}
69 data[c].fg = args.fg or theme.titlebar_fg_normal or theme.fg_normal
70 data[c].bg = args.bg or theme.titlebar_bg_normal or theme.bg_normal
71 data[c].fg_focus = args.fg_focus or theme.titlebar_fg_focus or theme.fg_focus
72 data[c].bg_focus = args.bg_focus or theme.titlebar_bg_focus or theme.bg_focus
73 data[c].width = args.width
74 data[c].font = args.font or theme.titlebar_font or theme.font
76 local tb = capi.wibox(args)
78 local title = capi.widget({ type = "textbox" })
79 if c.name then
80 title.text = "<span font_desc='" .. data[c].font .. "'> " ..
81 util.escape(c.name) .. " </span>"
82 end
84 -- Redirect relevant events to the client the titlebar belongs to
85 local bts = util.table.join(
86 abutton({ }, 1, button_callback_focus_raise_move),
87 abutton({ args.modkey }, 1, button_callback_move),
88 abutton({ args.modkey }, 3, button_callback_resize))
89 title:buttons(bts)
91 local appicon = capi.widget({ type = "imagebox" })
92 appicon.image = c.icon
94 -- for each button group, call create for the client.
95 -- if a button set is created add the set to the
96 -- data[c].button_sets for late updates and add the
97 -- individual buttons to the array part of the widget
98 -- list
99 local widget_list = {
100 layout = layout.horizontal.rightleft
102 local iw = 1
103 local is = 1
104 data[c].button_sets = {}
105 for i = 1, #button_groups do
106 local set = button_groups[i].create(c, args.modkey, theme)
107 if (set) then
108 data[c].button_sets[is] = set
109 is = is + 1
110 for n,b in pairs(set) do
111 widget_list[iw] = b
112 iw = iw + 1
117 tb.widgets = {
118 widget_list,
119 customwidget,
121 appicon = appicon,
122 title = title,
123 layout = layout.horizontal.flex
125 layout = layout.horizontal.rightleft
128 c.titlebar = tb
130 c:add_signal("property::icon", update)
131 c:add_signal("property::name", update)
132 c:add_signal("property::sticky", update)
133 c:add_signal("property::floating", update)
134 c:add_signal("property::ontop", update)
135 c:add_signal("property::maximized_vertical", update)
136 c:add_signal("property::maximized_horizontal", update)
137 update(c)
140 --- Update a titlebar. This should be called in some hooks.
141 -- @param c The client to update.
142 -- @param prop The property name which has changed.
143 function update(c)
144 if c.titlebar and data[c] then
145 local widgets = c.titlebar.widgets
146 if widgets[3].title then
147 widgets[3].title.text = "<span font_desc='" .. data[c].font ..
148 "'> ".. util.escape(c.name or "<unknown>") .. " </span>"
150 if widgets[3].appicon then
151 widgets[3].appicon.image = c.icon
153 if capi.client.focus == c then
154 c.titlebar.fg = data[c].fg_focus
155 c.titlebar.bg = data[c].bg_focus
156 else
157 c.titlebar.fg = data[c].fg
158 c.titlebar.bg = data[c].bg
161 -- iterated of all registered button_sets and update
162 local sets = data[c].button_sets
163 for i = 1, #sets do
164 sets[i].update(c,prop)
169 --- Remove a titlebar from a client.
170 -- @param c The client.
171 function remove(c)
172 c.titlebar = nil
173 data[c] = nil
176 -- Create a new button for the toolbar
177 -- @param c The client of the titlebar
178 -- @param name The base name of the button (i.e. close)
179 -- @param modkey ... you know that one, don't you?
180 -- @param theme The theme from beautifull. Used to get the image paths
181 -- @param state The state the button is associated to. Containse path the action and info about the image
182 local function button_new(c, name, modkey, theme, state)
183 local bts = abutton({ }, 1, nil, state.action)
185 -- get the image path from the theme. Only return a button if we find an image
186 local img
187 img = "titlebar_" .. name .. "_button_" .. state.img
188 img = theme[img]
189 if not img then return end
190 img = image(img)
191 if not img then return end
193 -- now create the button
194 local bname = name .. "_" .. state.idx
195 local button = widget.button({ image = img })
196 if not button then return end
197 local rbts = button:buttons()
199 for k, v in pairs(rbts) do
200 bts[#bts + 1] = v
203 button:buttons(bts)
204 button.visible = false
205 return button
208 -- Update the buttons in a button group
209 -- @param s The button group to update
210 -- @param c The client of the titlebar
211 -- @param p The property that has changed
212 local function button_group_update(s,c,p)
213 -- hide the currently active button, get the new state and show the new button
214 local n = s.select_state(c,p)
215 if n == nil then return end
216 if (s.active ~= nil) then s.active.visible = false end
217 s.active = s.buttons[n]
218 s.active.visible = true
221 -- Create all buttons in a group
222 -- @param c The client of the titlebar
223 -- @param group The button group to create the buttons for
224 -- @param modkey ...
225 -- @param theme Theme for the image paths
226 local function button_group_create(c, group, modkey, theme )
227 local s = {}
228 s.name = group.name
229 s.select_state = group.select_state
230 s.buttons = {
231 layout = layout.horizontal.rightleft
233 for n,state in pairs(group.states) do
234 s.buttons[n] = button_new(c, s.name, modkey, theme, state)
235 if (s.buttons[n] == nil) then return end
236 for a,v in pairs(group.attributes) do
237 s.buttons[n][a] = v
240 function s.update(c,p) button_group_update(s,c,p) end
241 return s
244 -- Builds a new button group
245 -- @param name The base name for the buttons in the group (i.e. "close")
246 -- @param attrs Common attributes for the buttons (i.e. {align = "right")
247 -- @param sfn State select function.
248 -- @param args The states of the button
249 local function button_group(name, attrs, sfn, ...)
250 local s = {}
251 s.name = name
252 s.select_state = sfn
253 s.attributes = attrs
254 s.states = {}
256 for i, state in pairs({...}) do
257 s.states[state.idx] = state
260 function s.create(c,modkey, theme) return button_group_create(c,s,modkey, theme) end
261 return s
264 -- Select a state for a client based on an attribute of the client and whether it has focus
265 -- @param c The client of the titlebar
266 -- @param p The property that has changed
267 -- @param a The property to check
268 local function select_state(c,p,a)
269 if (c == nil) then return "n/i" end
270 if capi.client.focus == c then
271 if c[a] then
272 return "f/a"
273 else
274 return "f/i"
276 else
277 if c[a] then
278 return "n/a"
279 else
280 return "n/i"
285 -- Select a state for a client based on whether it's floating or not
286 -- @param c The client of the titlebar
287 -- @param p The property that has changed
288 local function select_state_floating(c,p)
289 if not c then return end
290 if capi.client.focus == c then
291 if client.floating.get(c) then
292 return "f/a"
294 return "f/i"
296 if client.floating.get(c) then
297 return "n/a"
299 return "n/i"
302 -- Select a state for a client based on whether it's maximized or not
303 -- @param c The client of the titlebar
304 -- @param p The property that has changed
305 local function select_state_maximized(c,p)
306 if (c == nil) then return "n/i" end
307 if capi.client.focus == c then
308 if c.maximized_horizontal or c.maximized_vertical then
309 return "f/a"
310 else
311 return "f/i"
313 else
314 if c.maximized_horizontal or c.maximized_vertical then
315 return "n/a"
316 else
317 return "n/i"
322 -- Select a state for a client based on whether it has focus or not
323 -- @param c The client of the titlebar
324 -- @param p The property that has changed
325 local function select_state_focus(c,p)
326 if c and capi.client.focus == c then
327 return "f"
329 return "n"
332 -- These are the predefined button groups
333 -- A short explanation using 'close_buttons' as an example:
334 -- "close" : name of the button, the images for this button are taken from the
335 -- theme variables titlebar_close_button_...
336 -- { align ... : attributes of all the buttons
337 -- select_state_focus : This function returns a short string used to describe
338 -- the state. In this case either "n" or "f" depending on
339 -- the focus state of the client. These strings can be
340 -- choosen freely but the< must match one of the idx fuekds
341 -- of the states below
342 -- { idx = "n" ... : This is the state of the button for the 'unfocussed'
343 -- (normal) state. The idx = "n" parameter connects this
344 -- button to the return value of the 'select_state_focus'
345 -- function. The img = "normal" parameter is used to
346 -- determine its image. In this case the iamge is taken from
347 -- the theme variable "titlebar_close_button_normal".
348 -- Finally the last parameter is the action for mouse
349 -- button 1
351 local ontop_buttons = button_group("ontop",
352 { align = "right" },
353 function(c,p) return select_state(c, p, "ontop") end,
354 { idx = "n/i", img = "normal_inactive",
355 action = function(w, t) t.client.ontop = true end },
356 { idx = "f/i", img = "focus_inactive",
357 action = function(w, t) t.client.ontop = true end },
358 { idx = "n/a", img = "normal_active",
359 action = function(w, t) t.client.ontop = false end },
360 { idx = "f/a", img = "focus_active",
361 action = function(w, t) t.client.ontop = false end })
363 local sticky_buttons = button_group("sticky",
364 { align = "right" },
365 function(c,p) return select_state(c,p,"sticky") end,
366 { idx = "n/i", img = "normal_inactive",
367 action = function(w, t) t.client.sticky = true end },
368 { idx = "f/i", img = "focus_inactive",
369 action = function(w, t) t.client.sticky = true end },
370 { idx = "n/a", img = "normal_active",
371 action = function(w, t) t.client.sticky = false end },
372 { idx = "f/a", img = "focus_active",
373 action = function(w, t) t.client.sticky = false end })
375 local maximized_buttons = button_group("maximized",
376 { align = "right" },
377 select_state_maximized,
378 { idx = "n/i", img = "normal_inactive",
379 action = function(w, t) t.client.maximized_horizontal = true
380 t.client.maximized_vertical = true end },
381 { idx = "f/i", img = "focus_inactive",
382 action = function(w, t) t.client.maximized_horizontal = true
383 t.client.maximized_vertical = true end },
384 { idx = "n/a", img = "normal_active",
385 action = function(w, t) t.client.maximized_horizontal = false
386 t.client.maximized_vertical = false end },
387 { idx = "f/a", img = "focus_active",
388 action = function(w, t) t.client.maximized_horizontal = false
389 t.client.maximized_vertical = false end })
391 local close_buttons = button_group("close",
392 { align = "left" },
393 select_state_focus,
394 { idx = "n", img = "normal",
395 action = function (w, t) t.client:kill() end },
396 { idx = "f", img = "focus",
397 action = function (w, t) t.client:kill() end })
399 local function floating_update(w, t)
400 client.floating.toggle(t.client)
403 local floating_buttons = button_group("floating",
404 { align = "right"},
405 select_state_floating,
406 { idx = "n/i", img = "normal_inactive", action = floating_update },
407 { idx = "f/i", img = "focus_inactive", action = floating_update },
408 { idx = "n/a", img = "normal_active", action = floating_update },
409 { idx = "f/a", img = "focus_active", action = floating_update })
411 button_groups = { close_buttons,
412 ontop_buttons,
413 sticky_buttons,
414 maximized_buttons,
415 floating_buttons }
417 -- Register standards hooks
418 capi.client.add_signal("focus", update)
419 capi.client.add_signal("unfocus", update)
421 -- vim: filetype=lua:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:textwidth=80