1 ---------------------------------------------------------------------------
2 -- @author Julien Danjou <julien@danjou.info>
3 -- @copyright 2008 Julien Danjou
4 -- @release @AWESOME_VERSION@
5 ---------------------------------------------------------------------------
7 -- Grab environment we need
12 local setmetatable
= setmetatable
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")
33 local data
= setmetatable({}, { __mode
= 'k' })
35 -- Predeclaration for buttons
38 local function button_callback_focus_raise_move(w
, t
)
39 capi
.client
.focus
= t
.client
41 mouse
.client
.move(t
.client
)
44 local function button_callback_move(w
, t
)
45 return mouse
.client
.move(t
.client
)
48 local function button_callback_resize(w
, t
)
49 return mouse
.client
.resize(t
.client
)
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
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
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" })
80 title
.text
= "<span font_desc='" .. data
[c
].font
.. "'> " ..
81 util
.escape(c
.name
) .. " </span>"
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
))
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
100 layout
= layout
.horizontal
.rightleft
104 data
[c
].button_sets
= {}
105 for i
= 1, #button_groups
do
106 local set
= button_groups
[i
].create(c
, args
.modkey
, theme
)
108 data
[c
].button_sets
[is
] = set
110 for n
,b
in pairs(set
) do
123 layout
= layout
.horizontal
.flex
125 layout
= layout
.horizontal
.rightleft
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
)
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.
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
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
164 sets
[i
].update(c
,prop
)
169 --- Remove a titlebar from a client.
170 -- @param c The client.
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
187 img
= "titlebar_" .. name
.. "_button_" .. state
.img
189 if not img
then return end
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
204 button
.visible
= false
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
225 -- @param theme Theme for the image paths
226 local function button_group_create(c
, group
, modkey
, theme
)
229 s
.select_state
= group
.select_state
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
240 function s
.update(c
,p
) button_group_update(s
,c
,p
) end
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
, ...)
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
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
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
296 if client
.floating
.get(c
) then
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
314 if c
.maximized_horizontal
or c
.maximized_vertical
then
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
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
351 local ontop_buttons
= button_group("ontop",
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",
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",
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",
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",
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
,
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