awesome/lib/awful/wibox.lua.in

386 lines
13 KiB
Lua

---------------------------------------------------------------------------
-- @author Julien Danjou <julien@danjou.info>
-- @copyright 2009 Julien Danjou
-- @release @AWESOME_VERSION@
---------------------------------------------------------------------------
-- Grab environment we need
local capi =
{
awesome = awesome,
screen = screen,
wibox = wibox
}
local setmetatable = setmetatable
local ipairs = ipairs
local table = table
local type = type
local image = image
local hooks = require("awful.hooks")
--- Wibox module for awful.
module("awful.wibox")
-- Array of table with wiboxes inside.
-- It's an array so it is ordered.
local wiboxes = {}
hooks.user.create("wibox_position")
--- Get the workarea space without wiboxes geometry.
-- @param s The screen number.
-- @return The screen workarea.
function get_workarea(s)
local area = capi.screen[s].workarea
for _, wprop in ipairs(wiboxes) do
if wprop.wibox.visible and wprop.wibox.screen == s then
if wprop.position == "top" then
area.y = area.y + wprop.wibox:geometry().height + (2 * wprop.wibox.border_width)
area.height = area.height - (wprop.wibox:geometry().height + (2 * wprop.wibox.border_width))
elseif wprop.position == "bottom" then
area.height = area.height - (wprop.wibox:geometry().height + (2 * wprop.wibox.border_width))
elseif wprop.position == "left" then
area.x = area.x + (wprop.wibox:geometry().width + (2 * wprop.wibox.border_width))
area.width = area.width - (wprop.wibox:geometry().width + (2 * wprop.wibox.border_width))
elseif wprop.position == "right" then
area.width = area.width - (wprop.wibox:geometry().width + (2 * wprop.wibox.border_width))
end
end
end
return area
end
local function compute_area(wibox, position, s)
local s = wibox.screen or s or 1
local area = capi.screen[s].workarea
local ignore = false
for _, wprop in ipairs(wiboxes) do
if wprop.wibox == wibox then
ignore = true
elseif wprop.wibox.visible and wprop.wibox.screen == s
and not (ignore and wprop.position == position) then
if (wprop.position == "right" or wprop.position == "left")
and wprop.position == position then
area.x = area.x + (wibox:geometry().width + (2 * wibox.border_width))
elseif wprop.position == "top" then
if position == "top" then
area.y = area.y + (wprop.wibox:geometry().height + (2 * wprop.wibox.border_width))
elseif position == "left" or position == "right" then
area.height = area.height - (wprop.wibox:geometry().height + (2 * wprop.wibox.border_width))
area.y = area.y + (wprop.wibox:geometry().height + (2 * wprop.wibox.border_width))
end
elseif wprop.position == "bottom" then
if position == "bottom" then
area.y = area.y - (wprop.wibox:geometry().height + (2 * wprop.wibox.border_width))
elseif position == "left" or position == "right" then
area.height = area.height - (wprop.wibox:geometry().height + (2 * wprop.wibox.border_width))
end
end
end
end
return area
end
--- 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
--- Attach a wibox to a screen at the 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 area = compute_area(wibox, position, screen)
local wingeom = wibox: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
wingeom.x = area.x + area.width - (wingeom.width + 2 * wibox.border_width)
elseif position == "left" then
wingeom.x = area.x
elseif position == "bottom" then
wingeom.y = (area.y + area.height) - (wingeom.height + 2 * wibox.border_width)
elseif position == "top" then
wingeom.y = area.y
end
for _, wprop in ipairs(wiboxes) do
if wprop.wibox == wibox then
wprop.position = position
break
end
end
wibox:geometry(wingeom)
end
-- Reset all wiboxes positions.
local function update_all_wiboxes_position()
for _, wprop in ipairs(wiboxes) do
set_position(wprop.wibox, wprop.position)
hooks.user.call("wibox_position", wprop.wibox)
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 position ~= "top" and position ~= "bottom" and position ~= "right" and position ~= "left" then
position = "floating"
end
if not wibox_prop_table then
table.insert(wiboxes,
setmetatable({ wibox = wibox, position = position }, { __mode = 'v' }))
else
wibox_prop_table.position = position
end
if wibox.screen and wibox.visible then
update_all_wiboxes_position()
end
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 area = compute_area(wibox, position, screen)
local wingeom = wibox:geometry()
wingeom.height = wingeom.height + 2 * wibox.border_width
wingeom.width = wingeom.width + 2 * wibox.border_width
if position == "right" then
if align == "right" then
wingeom.y = area.y
elseif align == "left" then
wingeom.y = area.y + area.height - wingeom.height
elseif align == "center" then
wingeom.y = area.y + (area.height - wingeom.height) / 2
end
elseif position == "left" then
if align == "right" then
wingeom.y = (area.y + area.height) - wingeom.height
elseif align == "left" then
wingeom.y = area.y
elseif align == "center" then
wingeom.y = area.y + (area.height - wingeom.height) / 2
end
elseif position == "bottom" then
if align == "right" then
wingeom.x = area.x + area.width - wingeom.width
elseif align == "left" then
wingeom.x = area.x
elseif align == "center" then
wingeom.x = area.x + (area.width - wingeom.width) / 2
end
elseif position == "top" then
if align == "right" then
wingeom.x = area.x + area.width - wingeom.width
elseif align == "left" then
wingeom.x = area.x
elseif align == "center" then
wingeom.x = area.x + (area.width - wingeom.width) / 2
end
end
-- Reset height and width
wingeom.height = wingeom.height - 2 * wibox.border_width
wingeom.width = wingeom.width - 2 * wibox.border_width
wibox:geometry(wingeom)
end
--- Stretch a wibox so it takes all screen width or height.
-- @param wibox The wibox.
function stretch(wibox)
local position = get_position(wibox)
local area = compute_area(wibox, position)
local wingeom = {}
if position == "right" or position == "left" then
wingeom.height = area.height - (2 * wibox.border_width)
wibox:geometry(wingeom)
align(wibox, "center")
else
wingeom.width = area.width - (2 * wibox.border_width)
wibox:geometry(wingeom)
align(wibox, "left")
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
-- Empty position and align in arg so we are passing deprecation warning
arg.position = nil
-- 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 = arg.height:match("(%d+)%%")
if hp then
arg.height = capi.screen[arg.screen].workarea.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 = arg.width:match("(%d+)%%")
if wp then
arg.width = capi.screen[arg.screen].workarea.width * wp / 100
end
end
end
end
local w = capi.wibox(arg)
if position == "left" then
w.orientation = "north"
elseif position == "right" then
w.orientation = "south"
end
w.screen = arg.screen or 1
attach(w, position)
if has_to_stretch then
stretch(w)
else
align(w, arg.align)
end
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
local geometry = wibox:geometry()
local width = geometry.width
local height = geometry.height
-- Corners can't be larger than half the wibox' space
if width / 2 < corner_size then
corner_size = width / 2
end
if height / 2 < corner_size then
corner_size = height / 2
end
wibox.shape_clip = do_rounded_corners(width, height, corner_size)
wibox.shape_bounding = do_rounded_corners(width + border * 2, height + border * 2, corner_size + border)
end
local function update_wiboxes_position(obj, prop)
if (type(obj) == "wibox"
and (prop == nil
or prop == "visible"
or prop == "screen"))
or (type(obj) == "client"
and prop == "struts") then
update_all_wiboxes_position()
end
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.
hooks.property.register(update_wiboxes_position)
hooks.manage.register(update_wiboxes_on_struts)
hooks.unmanage.register(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