awful.widget.taglist: use attached_add_signal
[awesome.git] / lib / awful / wibox.lua.in
blob2069c60cbc477ae1e1c3462966af99ee1d383fd3
1 ---------------------------------------------------------------------------
2 -- @author Julien Danjou <julien@danjou.info>
3 -- @copyright 2009 Julien Danjou
4 -- @release @AWESOME_VERSION@
5 ---------------------------------------------------------------------------
7 -- Grab environment we need
8 local capi =
10 awesome = awesome,
11 screen = screen,
12 wibox = wibox,
13 client = client
15 local setmetatable = setmetatable
16 local ipairs = ipairs
17 local table = table
18 local type = type
19 local image = image
21 --- Wibox module for awful.
22 module("awful.wibox")
24 -- Array of table with wiboxes inside.
25 -- It's an array so it is ordered.
26 local wiboxes = {}
28 --- Get a wibox position if it has been set, or return top.
29 -- @param wibox The wibox
30 -- @return The wibox position.
31 function get_position(wibox)
32 for _, wprop in ipairs(wiboxes) do
33 if wprop.wibox == wibox then
34 return wprop.position
35 end
36 end
37 return "top"
38 end
40 --- Put a wibox on a screen at this position.
41 -- @param wibox The wibox to attach.
42 -- @param position The position: top, bottom left or right.
43 -- @param screen If the wibox it not attached to a screen, specified on which
44 -- screen the position should be set.
45 function set_position(wibox, position, screen)
46 local screen = screen or wibox.screen or 1
47 local area = capi.screen[screen].geometry
49 -- The "length" of a wibox is always chosen to be the optimal size
50 -- (non-floating).
51 -- The "width" of a wibox is kept if it exists.
52 if position == "right" then
53 wibox.x = area.x + area.width - (wibox.width + 2 * wibox.border_width)
54 elseif position == "left" then
55 wibox.x = area.x
56 elseif position == "bottom" then
57 wibox.y = (area.y + area.height) - (wibox.height + 2 * wibox.border_width)
58 elseif position == "top" then
59 wibox.y = area.y
60 end
62 for _, wprop in ipairs(wiboxes) do
63 if wprop.wibox == wibox then
64 wprop.position = position
65 break
66 end
67 end
68 end
70 -- Reset all wiboxes positions.
71 local function update_all_wiboxes_position()
72 for _, wprop in ipairs(wiboxes) do
73 set_position(wprop.wibox, wprop.position)
74 end
75 end
77 local function call_wibox_position_hook_on_prop_update(w)
78 update_all_wiboxes_position()
79 end
81 local function wibox_update_strut(wibox)
82 for _, wprop in ipairs(wiboxes) do
83 if wprop.wibox == wibox then
84 if wprop.position == "top" then
85 wibox:struts { left = 0, right = 0, bottom = 0, top = wibox.height }
86 elseif wprop.position == "bottom" then
87 wibox:struts { left = 0, right = 0, bottom = wibox.height, top = 0 }
88 elseif wprop.position == "left" then
89 wibox:struts { left = wibox.width, right = 0, bottom = 0, top = 0 }
90 elseif wprop.position == "right" then
91 wibox:struts { left = 0, right = wibox.width, bottom = 0, top = 0 }
92 end
93 end
94 end
95 end
97 --- Attach a wibox to a screen.
98 -- If a wibox is attached, it will be automatically be moved when other wiboxes
99 -- will be attached.
100 -- @param wibox The wibox to attach.
101 -- @param position The position of the wibox: top, bottom, left or right.
102 function attach(wibox, position)
103 -- Store wibox as attached in a weak-valued table
104 local wibox_prop_table
105 -- Start from end since we sometimes remove items
106 for i = #wiboxes, 1, -1 do
107 -- Since wiboxes are stored as weak value, they can disappear.
108 -- If they did, remove their entries
109 if wiboxes[i].wibox == nil then
110 table.remove(wiboxes, i)
111 elseif wiboxes[i].wibox == wibox then
112 wibox_prop_table = wiboxes[i]
113 -- We could break here, but well, let's check if there is no other
114 -- table with their wiboxes been garbage collected.
118 if not wibox_prop_table then
119 table.insert(wiboxes, setmetatable({ wibox = wibox, position = position }, { __mode = 'v' }))
120 else
121 wibox_prop_table.position = position
124 wibox:add_signal("property::width", wibox_update_strut)
125 wibox:add_signal("property::height", wibox_update_strut)
127 wibox:add_signal("property::screen", call_wibox_position_hook_on_prop_update)
128 wibox:add_signal("property::visible", call_wibox_position_hook_on_prop_update)
129 wibox:add_signal("property::border_width", call_wibox_position_hook_on_prop_update)
132 --- Align a wibox.
133 -- @param wibox The wibox.
134 -- @param align The alignment: left, right or center.
135 -- @param screen If the wibox is not attached to any screen, you can specify the
136 -- screen where to align. Otherwise 1 is assumed.
137 function align(wibox, align, screen)
138 local position = get_position(wibox)
139 local screen = screen or wibox.screen or 1
140 local area = capi.screen[screen].geometry
142 if position == "right" then
143 if align == "right" then
144 wibox.y = area.y
145 elseif align == "left" then
146 wibox.y = area.y + area.height - (wibox.height + 2 * wibox.border_width)
147 elseif align == "center" then
148 wibox.y = area.y + (area.height - wibox.height) / 2
150 elseif position == "left" then
151 if align == "right" then
152 wibox.y = (area.y + area.height) - (wibox.height + 2 * wibox.border_width)
153 elseif align == "left" then
154 wibox.y = area.y
155 elseif align == "center" then
156 wibox.y = area.y + (area.height - wibox.height) / 2
158 elseif position == "bottom" then
159 if align == "right" then
160 wibox.x = area.x + area.width - (wibox.width + 2 * wibox.border_width)
161 elseif align == "left" then
162 wibox.x = area.x
163 elseif align == "center" then
164 wibox.x = area.x + (area.width - wibox.width) / 2
166 elseif position == "top" then
167 if align == "right" then
168 wibox.x = area.x + area.width - (wibox.width + 2 * wibox.border_width)
169 elseif align == "left" then
170 wibox.x = area.x
171 elseif align == "center" then
172 wibox.x = area.x + (area.width - wibox.width) / 2
177 --- Stretch a wibox so it takes all screen width or height.
178 -- @param wibox The wibox.
179 -- @param screen The screen to stretch on, or the wibox screen.
180 function stretch(wibox, screen)
181 local screen = screen or wibox.screen
182 if screen then
183 local position = get_position(wibox)
184 local area = capi.screen[screen].workarea
185 if position == "right" or position == "left" then
186 wibox.height = area.height - (2 * wibox.border_width)
187 align(wibox, "center")
188 else
189 wibox.width = area.width - (2 * wibox.border_width)
190 align(wibox, "left")
195 --- Create a new wibox and attach it to a screen edge.
196 -- @see capi.wibox
197 -- @param args A table with standard arguments to wibox() creator.
198 -- You can add also position key with value top, bottom, left or right.
199 -- You can also use width or height in % and set align to center, right or left.
200 -- You can also set the screen key with a screen number to attach the wibox.
201 -- If not specified, 1 is assumed.
202 -- @return The wibox created.
203 function new(arg)
204 local arg = arg or {}
205 local position = arg.position or "top"
206 local has_to_stretch = true
207 -- Empty position and align in arg so we are passing deprecation warning
208 arg.position = nil
210 -- Set default size
211 if position == "left" or position == "right" then
212 arg.width = arg.width or capi.awesome.font_height * 1.5
213 if arg.height then
214 has_to_stretch = false
215 if arg.screen then
216 local hp = arg.height:match("(%d+)%%")
217 if hp then
218 arg.height = capi.screen[arg.screen].geometry.height * hp / 100
222 else
223 arg.height = arg.height or capi.awesome.font_height * 1.5
224 if arg.width then
225 has_to_stretch = false
226 if arg.screen then
227 local wp = arg.width:match("(%d+)%%")
228 if wp then
229 arg.width = capi.screen[arg.screen].geometry.width * wp / 100
235 local w = capi.wibox(arg)
237 if position == "left" then
238 w.orientation = "north"
239 elseif position == "right" then
240 w.orientation = "south"
243 w.screen = arg.screen or 1
245 attach(w, position)
246 if has_to_stretch then
247 stretch(w)
248 else
249 align(w, arg.align)
252 return w
255 local function do_rounded_corners(width, height, corner)
256 local img = image.argb32(width, height, nil)
258 -- The image starts completely black which is fully opaque for our use
260 local function transp_rect(x, y)
261 img:draw_rectangle(x, y, corner, corner, true, "#ffffff")
263 local function opaque_circle(x, y)
264 -- x, y are the center of the circle
265 img:draw_circle(x, y, corner, corner, true, "#000000")
268 -- Upper left corner
269 -- First make a 'corner times corner' rectangle transparent
270 transp_rect(0, 0)
271 -- Then add the rounded corner
272 opaque_circle(corner, corner)
274 -- Upper right corner
275 transp_rect(width - corner, 0)
276 opaque_circle(width - corner - 1, corner)
278 -- Bottom left corner
279 transp_rect(0, height - corner)
280 opaque_circle(corner, height - corner - 1)
282 -- Bottom right corner
283 transp_rect(width - corner, height - corner)
284 opaque_circle(width - corner - 1, height - corner - 1)
286 return img
289 --- Add rounded corners to a wibox
290 -- @param wibox The wibox.
291 -- @param corner_size The size in pixel of the rounded corners.
292 function rounded_corners(wibox, corner_size)
293 local border = wibox.border_width
295 -- Corners can't be larger than half the wibox' space
296 if wibox.width / 2 < corner_size then
297 corner_size = wibox.width / 2
299 if wibox.height / 2 < corner_size then
300 corner_size = wibox.height / 2
303 wibox.shape_clip = do_rounded_corners(wibox.width, wibox.height, corner_size)
304 wibox.shape_bounding = do_rounded_corners(wibox.width + border * 2, wibox.height + border * 2, corner_size + border)
307 local function update_wiboxes_on_struts(c)
308 local struts = c:struts()
309 if struts.left ~= 0 or struts.right ~= 0
310 or struts.top ~= 0 or struts.bottom ~= 0 then
311 update_all_wiboxes_position()
315 -- Hook registered to reset all wiboxes position.
316 capi.client.add_signal("manage", function(c)
317 update_wiboxes_on_struts(c)
318 c:add_signal("property::struts", update_wiboxes_on_struts)
319 end)
320 capi.client.add_signal("unmanage", update_wiboxes_on_struts)
322 setmetatable(_M, { __call = function(_, ...) return new(...) end })
324 -- vim: filetype=lua:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:encoding=utf-8:textwidth=80