--------------------------------------------------------------------------- -- @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 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" -- 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 arg.height = arg.height or 100 else arg.width = arg.width or 100 arg.height = arg.height or capi.awesome.font_height * 1.5 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) stretch(w) 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