awesome/lib/awful/wibox.lua.in
Adrian C. (anrxc) 4119e92d82 awful.wibox: update struts on align()
Setting width or height (horizontal or vertical) to an arbitrary value
causes the wibox not to be stretched. Instead the align() function is
called, which might not modify any wibox property in many situations,
or none when align parameter was not provided by the user. The major
side effect is that wibox struts were never updated and clients
covered the wibox (until a first signal handler caused struts to be
updated).

Signed-off-by: Adrian C. (anrxc) <anrxc@sysphere.org>
Signed-off-by: Julien Danjou <julien@danjou.info>
2010-03-23 11:06:32 +01:00

337 lines
11 KiB
Lua

---------------------------------------------------------------------------
-- @author Julien Danjou &lt;julien@danjou.info&gt;
-- @copyright 2009 Julien Danjou
-- @release @AWESOME_VERSION@
---------------------------------------------------------------------------
-- Grab environment we need
local capi =
{
awesome = awesome,
screen = screen,
wibox = wibox,
client = client
}
local setmetatable = setmetatable
local tostring = tostring
local ipairs = ipairs
local table = table
local type = type
local image = image
local error = error
--- Wibox module for awful.
-- This module allows you to easily create wibox and attach them to the edge of
-- a screen.
module("awful.wibox")
-- Array of table with wiboxes inside.
-- It's an array so it is ordered.
local wiboxes = {}
--- Get a wibox position if it has been set, or return top.
-- @param wibox The wibox
-- @return The wibox position.
function get_position(wibox)
for _, wprop in ipairs(wiboxes) do
if wprop.wibox == wibox then
return wprop.position
end
end
return "top"
end
--- Put a wibox on a screen at this position.
-- @param wibox The wibox to attach.
-- @param position The position: top, bottom left or right.
-- @param screen If the wibox it not attached to a screen, specified on which
-- screen the position should be set.
function set_position(wibox, position, screen)
local screen = screen or wibox.screen or 1
local area = capi.screen[screen].geometry
-- The "length" of a wibox is always chosen to be the optimal size
-- (non-floating).
-- The "width" of a wibox is kept if it exists.
if position == "right" then
wibox.x = area.x + area.width - (wibox.width + 2 * wibox.border_width)
elseif position == "left" then
wibox.x = area.x
elseif position == "bottom" then
wibox.y = (area.y + area.height) - (wibox.height + 2 * wibox.border_width)
elseif position == "top" then
wibox.y = area.y
end
for _, wprop in ipairs(wiboxes) do
if wprop.wibox == wibox then
wprop.position = position
break
end
end
end
-- Reset all wiboxes positions.
local function update_all_wiboxes_position()
for _, wprop in ipairs(wiboxes) do
set_position(wprop.wibox, wprop.position)
end
end
local function call_wibox_position_hook_on_prop_update(w)
update_all_wiboxes_position()
end
local function wibox_update_strut(wibox)
for _, wprop in ipairs(wiboxes) do
if wprop.wibox == wibox then
if not wibox.visible then
wibox:struts { left = 0, right = 0, bottom = 0, top = 0 }
elseif wprop.position == "top" then
wibox:struts { left = 0, right = 0, bottom = 0, top = wibox.height + 2 * wibox.border_width }
elseif wprop.position == "bottom" then
wibox:struts { left = 0, right = 0, bottom = wibox.height + 2 * wibox.border_width, top = 0 }
elseif wprop.position == "left" then
wibox:struts { left = wibox.width + 2 * wibox.border_width, right = 0, bottom = 0, top = 0 }
elseif wprop.position == "right" then
wibox:struts { left = 0, right = wibox.width + 2 * wibox.border_width, bottom = 0, top = 0 }
end
break
end
end
end
--- Attach a wibox to a screen.
-- If a wibox is attached, it will be automatically be moved when other wiboxes
-- will be attached.
-- @param wibox The wibox to attach.
-- @param position The position of the wibox: top, bottom, left or right.
function attach(wibox, position)
-- Store wibox as attached in a weak-valued table
local wibox_prop_table
-- Start from end since we sometimes remove items
for i = #wiboxes, 1, -1 do
-- Since wiboxes are stored as weak value, they can disappear.
-- If they did, remove their entries
if wiboxes[i].wibox == nil then
table.remove(wiboxes, i)
elseif wiboxes[i].wibox == wibox then
wibox_prop_table = wiboxes[i]
-- We could break here, but well, let's check if there is no other
-- table with their wiboxes been garbage collected.
end
end
if not wibox_prop_table then
table.insert(wiboxes, setmetatable({ wibox = wibox, position = position }, { __mode = 'v' }))
else
wibox_prop_table.position = position
end
wibox:add_signal("property::width", wibox_update_strut)
wibox:add_signal("property::height", wibox_update_strut)
wibox:add_signal("property::visible", wibox_update_strut)
wibox:add_signal("property::screen", call_wibox_position_hook_on_prop_update)
wibox:add_signal("property::width", call_wibox_position_hook_on_prop_update)
wibox:add_signal("property::height", call_wibox_position_hook_on_prop_update)
wibox:add_signal("property::visible", call_wibox_position_hook_on_prop_update)
wibox:add_signal("property::border_width", call_wibox_position_hook_on_prop_update)
end
--- Align a wibox.
-- @param wibox The wibox.
-- @param align The alignment: left, right or center.
-- @param screen If the wibox is not attached to any screen, you can specify the
-- screen where to align. Otherwise 1 is assumed.
function align(wibox, align, screen)
local position = get_position(wibox)
local screen = screen or wibox.screen or 1
local area = capi.screen[screen].workarea
if position == "right" then
if align == "right" then
wibox.y = area.y
elseif align == "left" then
wibox.y = area.y + area.height - (wibox.height + 2 * wibox.border_width)
elseif align == "center" then
wibox.y = area.y + (area.height - wibox.height) / 2
end
elseif position == "left" then
if align == "right" then
wibox.y = (area.y + area.height) - (wibox.height + 2 * wibox.border_width)
elseif align == "left" then
wibox.y = area.y
elseif align == "center" then
wibox.y = area.y + (area.height - wibox.height) / 2
end
elseif position == "bottom" then
if align == "right" then
wibox.x = area.x + area.width - (wibox.width + 2 * wibox.border_width)
elseif align == "left" then
wibox.x = area.x
elseif align == "center" then
wibox.x = area.x + (area.width - wibox.width) / 2
end
elseif position == "top" then
if align == "right" then
wibox.x = area.x + area.width - (wibox.width + 2 * wibox.border_width)
elseif align == "left" then
wibox.x = area.x
elseif align == "center" then
wibox.x = area.x + (area.width - wibox.width) / 2
end
end
-- Update struts regardless of changes
wibox_update_strut(wibox)
end
--- Stretch a wibox so it takes all screen width or height.
-- @param wibox The wibox.
-- @param screen The screen to stretch on, or the wibox screen.
function stretch(wibox, screen)
local screen = screen or wibox.screen
if screen then
local position = get_position(wibox)
local area = capi.screen[screen].workarea
if position == "right" or position == "left" then
wibox.height = area.height - (2 * wibox.border_width)
wibox.y = area.y
else
wibox.width = area.width - (2 * wibox.border_width)
wibox.x = area.x
end
end
end
--- Create a new wibox and attach it to a screen edge.
-- @see capi.wibox
-- @param args A table with standard arguments to wibox() creator.
-- You can add also position key with value top, bottom, left or right.
-- You can also use width or height in % and set align to center, right or left.
-- You can also set the screen key with a screen number to attach the wibox.
-- If not specified, 1 is assumed.
-- @return The wibox created.
function new(arg)
local arg = arg or {}
local position = arg.position or "top"
local has_to_stretch = true
if position ~= "top" and position ~="bottom"
and position ~= "left" and position ~= "right" then
error("Invalid position in awful.wibox(), you may only use"
.. " 'top', 'bottom', 'left' and 'right'")
end
-- Set default size
if position == "left" or position == "right" then
arg.width = arg.width or capi.awesome.font_height * 1.5
if arg.height then
has_to_stretch = false
if arg.screen then
local hp = tostring(arg.height):match("(%d+)%%")
if hp then
arg.height = capi.screen[arg.screen].geometry.height * hp / 100
end
end
end
else
arg.height = arg.height or capi.awesome.font_height * 1.5
if arg.width then
has_to_stretch = false
if arg.screen then
local wp = tostring(arg.width):match("(%d+)%%")
if wp then
arg.width = capi.screen[arg.screen].geometry.width * wp / 100
end
end
end
end
local w = capi.wibox(arg)
w.screen = arg.screen or 1
attach(w, position)
if has_to_stretch then
stretch(w)
else
align(w, arg.align)
end
set_position(w, position)
return w
end
local function do_rounded_corners(width, height, corner)
local img = image.argb32(width, height, nil)
-- The image starts completely black which is fully opaque for our use
local function transp_rect(x, y)
img:draw_rectangle(x, y, corner, corner, true, "#ffffff")
end
local function opaque_circle(x, y)
-- x, y are the center of the circle
img:draw_circle(x, y, corner, corner, true, "#000000")
end
-- Upper left corner
-- First make a 'corner times corner' rectangle transparent
transp_rect(0, 0)
-- Then add the rounded corner
opaque_circle(corner, corner)
-- Upper right corner
transp_rect(width - corner, 0)
opaque_circle(width - corner - 1, corner)
-- Bottom left corner
transp_rect(0, height - corner)
opaque_circle(corner, height - corner - 1)
-- Bottom right corner
transp_rect(width - corner, height - corner)
opaque_circle(width - corner - 1, height - corner - 1)
return img
end
--- Add rounded corners to a wibox
-- @param wibox The wibox.
-- @param corner_size The size in pixel of the rounded corners.
function rounded_corners(wibox, corner_size)
local border = wibox.border_width
-- Corners can't be larger than half the wibox' space
if wibox.width / 2 < corner_size then
corner_size = wibox.width / 2
end
if wibox.height / 2 < corner_size then
corner_size = wibox.height / 2
end
wibox.shape_clip = do_rounded_corners(wibox.width, wibox.height, corner_size)
wibox.shape_bounding = do_rounded_corners(wibox.width + border * 2, wibox.height + border * 2, corner_size + border)
end
local function update_wiboxes_on_struts(c)
local struts = c:struts()
if struts.left ~= 0 or struts.right ~= 0
or struts.top ~= 0 or struts.bottom ~= 0 then
update_all_wiboxes_position()
end
end
-- Hook registered to reset all wiboxes position.
capi.client.add_signal("manage", function(c)
update_wiboxes_on_struts(c)
c:add_signal("property::struts", update_wiboxes_on_struts)
end)
capi.client.add_signal("unmanage", update_wiboxes_on_struts)
setmetatable(_M, { __call = function(_, ...) return new(...) end })
-- vim: filetype=lua:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:encoding=utf-8:textwidth=80