awful.wibox: Error out on invalid positions
[awesome.git] / lib / awful / wibox.lua.in
blob1bae18b537579ab545649f8a2c71d35f64a50162
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
20 local error = error
22 --- Wibox module for awful.
23 -- This module allows you to easily create wibox and attach them to the edge of
24 -- a screen.
25 module("awful.wibox")
27 -- Array of table with wiboxes inside.
28 -- It's an array so it is ordered.
29 local wiboxes = {}
31 --- Get a wibox position if it has been set, or return top.
32 -- @param wibox The wibox
33 -- @return The wibox position.
34 function get_position(wibox)
35 for _, wprop in ipairs(wiboxes) do
36 if wprop.wibox == wibox then
37 return wprop.position
38 end
39 end
40 return "top"
41 end
43 --- Put a wibox on a screen at this position.
44 -- @param wibox The wibox to attach.
45 -- @param position The position: top, bottom left or right.
46 -- @param screen If the wibox it not attached to a screen, specified on which
47 -- screen the position should be set.
48 function set_position(wibox, position, screen)
49 local screen = screen or wibox.screen or 1
50 local area = capi.screen[screen].geometry
52 -- The "length" of a wibox is always chosen to be the optimal size
53 -- (non-floating).
54 -- The "width" of a wibox is kept if it exists.
55 if position == "right" then
56 wibox.x = area.x + area.width - (wibox.width + 2 * wibox.border_width)
57 elseif position == "left" then
58 wibox.x = area.x
59 elseif position == "bottom" then
60 wibox.y = (area.y + area.height) - (wibox.height + 2 * wibox.border_width)
61 elseif position == "top" then
62 wibox.y = area.y
63 end
65 for _, wprop in ipairs(wiboxes) do
66 if wprop.wibox == wibox then
67 wprop.position = position
68 break
69 end
70 end
71 end
73 -- Reset all wiboxes positions.
74 local function update_all_wiboxes_position()
75 for _, wprop in ipairs(wiboxes) do
76 set_position(wprop.wibox, wprop.position)
77 end
78 end
80 local function call_wibox_position_hook_on_prop_update(w)
81 update_all_wiboxes_position()
82 end
84 local function wibox_update_strut(wibox)
85 for _, wprop in ipairs(wiboxes) do
86 if wprop.wibox == wibox then
87 if not wibox.visible then
88 wibox:struts { left = 0, right = 0, bottom = 0, top = 0 }
89 elseif wprop.position == "top" then
90 wibox:struts { left = 0, right = 0, bottom = 0, top = wibox.height }
91 elseif wprop.position == "bottom" then
92 wibox:struts { left = 0, right = 0, bottom = wibox.height, top = 0 }
93 elseif wprop.position == "left" then
94 wibox:struts { left = wibox.width, right = 0, bottom = 0, top = 0 }
95 elseif wprop.position == "right" then
96 wibox:struts { left = 0, right = wibox.width, bottom = 0, top = 0 }
97 end
98 break
99 end
103 --- Attach a wibox to a screen.
104 -- If a wibox is attached, it will be automatically be moved when other wiboxes
105 -- will be attached.
106 -- @param wibox The wibox to attach.
107 -- @param position The position of the wibox: top, bottom, left or right.
108 function attach(wibox, position)
109 -- Store wibox as attached in a weak-valued table
110 local wibox_prop_table
111 -- Start from end since we sometimes remove items
112 for i = #wiboxes, 1, -1 do
113 -- Since wiboxes are stored as weak value, they can disappear.
114 -- If they did, remove their entries
115 if wiboxes[i].wibox == nil then
116 table.remove(wiboxes, i)
117 elseif wiboxes[i].wibox == wibox then
118 wibox_prop_table = wiboxes[i]
119 -- We could break here, but well, let's check if there is no other
120 -- table with their wiboxes been garbage collected.
124 if not wibox_prop_table then
125 table.insert(wiboxes, setmetatable({ wibox = wibox, position = position }, { __mode = 'v' }))
126 else
127 wibox_prop_table.position = position
130 wibox:add_signal("property::width", wibox_update_strut)
131 wibox:add_signal("property::height", wibox_update_strut)
132 wibox:add_signal("property::visible", wibox_update_strut)
134 wibox:add_signal("property::screen", call_wibox_position_hook_on_prop_update)
135 wibox:add_signal("property::visible", call_wibox_position_hook_on_prop_update)
136 wibox:add_signal("property::border_width", call_wibox_position_hook_on_prop_update)
139 --- Align a wibox.
140 -- @param wibox The wibox.
141 -- @param align The alignment: left, right or center.
142 -- @param screen If the wibox is not attached to any screen, you can specify the
143 -- screen where to align. Otherwise 1 is assumed.
144 function align(wibox, align, screen)
145 local position = get_position(wibox)
146 local screen = screen or wibox.screen or 1
147 local area = capi.screen[screen].geometry
149 if position == "right" then
150 if align == "right" then
151 wibox.y = area.y
152 elseif align == "left" then
153 wibox.y = area.y + area.height - (wibox.height + 2 * wibox.border_width)
154 elseif align == "center" then
155 wibox.y = area.y + (area.height - wibox.height) / 2
157 elseif position == "left" then
158 if align == "right" then
159 wibox.y = (area.y + area.height) - (wibox.height + 2 * wibox.border_width)
160 elseif align == "left" then
161 wibox.y = area.y
162 elseif align == "center" then
163 wibox.y = area.y + (area.height - wibox.height) / 2
165 elseif position == "bottom" then
166 if align == "right" then
167 wibox.x = area.x + area.width - (wibox.width + 2 * wibox.border_width)
168 elseif align == "left" then
169 wibox.x = area.x
170 elseif align == "center" then
171 wibox.x = area.x + (area.width - wibox.width) / 2
173 elseif position == "top" then
174 if align == "right" then
175 wibox.x = area.x + area.width - (wibox.width + 2 * wibox.border_width)
176 elseif align == "left" then
177 wibox.x = area.x
178 elseif align == "center" then
179 wibox.x = area.x + (area.width - wibox.width) / 2
184 --- Stretch a wibox so it takes all screen width or height.
185 -- @param wibox The wibox.
186 -- @param screen The screen to stretch on, or the wibox screen.
187 function stretch(wibox, screen)
188 local screen = screen or wibox.screen
189 if screen then
190 local position = get_position(wibox)
191 local area = capi.screen[screen].workarea
192 if position == "right" or position == "left" then
193 wibox.height = area.height - (2 * wibox.border_width)
194 align(wibox, "center")
195 else
196 wibox.width = area.width - (2 * wibox.border_width)
197 align(wibox, "left")
202 --- Create a new wibox and attach it to a screen edge.
203 -- @see capi.wibox
204 -- @param args A table with standard arguments to wibox() creator.
205 -- You can add also position key with value top, bottom, left or right.
206 -- You can also use width or height in % and set align to center, right or left.
207 -- You can also set the screen key with a screen number to attach the wibox.
208 -- If not specified, 1 is assumed.
209 -- @return The wibox created.
210 function new(arg)
211 local arg = arg or {}
212 local position = arg.position or "top"
213 local has_to_stretch = true
214 -- Empty position and align in arg so we are passing deprecation warning
215 arg.position = nil
217 if position ~= "top" and position ~="bottom"
218 and position ~= "left" and position ~= "right" then
219 error("Invalid position in awful.wibox(), you may only use"
220 .. " 'top', 'bottom', 'left' and 'right'")
223 -- Set default size
224 if position == "left" or position == "right" then
225 arg.width = arg.width or capi.awesome.font_height * 1.5
226 if arg.height then
227 has_to_stretch = false
228 if arg.screen then
229 local hp = arg.height:match("(%d+)%%")
230 if hp then
231 arg.height = capi.screen[arg.screen].geometry.height * hp / 100
235 else
236 arg.height = arg.height or capi.awesome.font_height * 1.5
237 if arg.width then
238 has_to_stretch = false
239 if arg.screen then
240 local wp = arg.width:match("(%d+)%%")
241 if wp then
242 arg.width = capi.screen[arg.screen].geometry.width * wp / 100
248 local w = capi.wibox(arg)
250 if position == "left" then
251 w.orientation = "north"
252 elseif position == "right" then
253 w.orientation = "south"
256 w.screen = arg.screen or 1
258 attach(w, position)
259 if has_to_stretch then
260 stretch(w)
261 else
262 align(w, arg.align)
265 set_position(w, position)
267 return w
270 local function do_rounded_corners(width, height, corner)
271 local img = image.argb32(width, height, nil)
273 -- The image starts completely black which is fully opaque for our use
275 local function transp_rect(x, y)
276 img:draw_rectangle(x, y, corner, corner, true, "#ffffff")
278 local function opaque_circle(x, y)
279 -- x, y are the center of the circle
280 img:draw_circle(x, y, corner, corner, true, "#000000")
283 -- Upper left corner
284 -- First make a 'corner times corner' rectangle transparent
285 transp_rect(0, 0)
286 -- Then add the rounded corner
287 opaque_circle(corner, corner)
289 -- Upper right corner
290 transp_rect(width - corner, 0)
291 opaque_circle(width - corner - 1, corner)
293 -- Bottom left corner
294 transp_rect(0, height - corner)
295 opaque_circle(corner, height - corner - 1)
297 -- Bottom right corner
298 transp_rect(width - corner, height - corner)
299 opaque_circle(width - corner - 1, height - corner - 1)
301 return img
304 --- Add rounded corners to a wibox
305 -- @param wibox The wibox.
306 -- @param corner_size The size in pixel of the rounded corners.
307 function rounded_corners(wibox, corner_size)
308 local border = wibox.border_width
310 -- Corners can't be larger than half the wibox' space
311 if wibox.width / 2 < corner_size then
312 corner_size = wibox.width / 2
314 if wibox.height / 2 < corner_size then
315 corner_size = wibox.height / 2
318 wibox.shape_clip = do_rounded_corners(wibox.width, wibox.height, corner_size)
319 wibox.shape_bounding = do_rounded_corners(wibox.width + border * 2, wibox.height + border * 2, corner_size + border)
322 local function update_wiboxes_on_struts(c)
323 local struts = c:struts()
324 if struts.left ~= 0 or struts.right ~= 0
325 or struts.top ~= 0 or struts.bottom ~= 0 then
326 update_all_wiboxes_position()
330 -- Hook registered to reset all wiboxes position.
331 capi.client.add_signal("manage", function(c)
332 update_wiboxes_on_struts(c)
333 c:add_signal("property::struts", update_wiboxes_on_struts)
334 end)
335 capi.client.add_signal("unmanage", update_wiboxes_on_struts)
337 setmetatable(_M, { __call = function(_, ...) return new(...) end })
339 -- vim: filetype=lua:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:encoding=utf-8:textwidth=80