awesome/lib/awful/wibar.lua

468 lines
14 KiB
Lua
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

---------------------------------------------------------------------------
--- Wibox module for awful.
-- This module allows you to easily create wibox and attach them to the edge of
-- a screen.
--
-- @author Emmanuel Lepage Vallee <elv1313@gmail.com>
-- @copyright 2016 Emmanuel Lepage Vallee
-- @classmod awful.wibar
---------------------------------------------------------------------------
-- Grab environment we need
local capi =
{
screen = screen,
client = client
}
local setmetatable = setmetatable
local tostring = tostring
local ipairs = ipairs
local error = error
local wibox = require("wibox")
local beautiful = require("beautiful")
local gdebug = require("gears.debug")
local placement = require("awful.placement")
local function get_screen(s)
return s and capi.screen[s]
end
local awfulwibar = { mt = {} }
--- Array of table with wiboxes inside.
-- It's an array so it is ordered.
local wiboxes = setmetatable({}, {__mode = "v"})
--- If the wibar needs to be stretched to fill the screen.
-- @property stretch
-- @tparam boolean stretch
--- The wibar's width.
-- @property width
-- @tparam integer width
--- The wibar's height.
-- @property height
-- @tparam integer height
--- If the wibar needs to be stretched to fill the screen.
-- @beautiful beautiful.wibar_stretch
-- @tparam boolean stretch
--- The wibar border width.
-- @beautiful beautiful.wibar_border_width
-- @tparam integer border_width
--- The wibar border color.
-- @beautiful beautiful.wibar_border_color
-- @tparam string border_color
--- If the wibar is to be on top of other windows.
-- @beautiful beautiful.wibar_ontop
-- @tparam boolean ontop
--- The wibar's mouse cursor.
-- @beautiful beautiful.wibar_cursor
-- @tparam string cursor
--- The wibar opacity, between 0 and 1.
-- @beautiful beautiful.wibar_opacity
-- @tparam number opacity
--- The window type (desktop, normal, dock, …).
-- @beautiful beautiful.wibar_type
-- @tparam string type
--- The wibar's width.
-- @beautiful beautiful.wibar_width
-- @tparam integer width
--- The wibar's height.
-- @beautiful beautiful.wibar_height
-- @tparam integer height
--- The wibar's background color.
-- @beautiful beautiful.wibar_bg
-- @tparam color bg
--- The wibar's background image.
-- @beautiful beautiful.wibar_bgimage
-- @tparam surface bgimage
--- The wibar's foreground (text) color.
-- @beautiful beautiful.wibar_fg
-- @tparam color fg
--- The wibar's shape.
-- @beautiful beautiful.wibar_shape
-- @tparam gears.shape shape
-- Compute the margin on one side
local function get_margin(w, position, auto_stop)
local h_or_w = (position == "top" or position == "bottom") and "height" or "width"
local ret = 0
for _, v in ipairs(wiboxes) do
-- Ignore the wibars placed after this one
if auto_stop and v == w then break end
if v.position == position and v.screen == w.screen and v.visible then
ret = ret + v[h_or_w]
end
end
return ret
end
-- `honor_workarea` cannot be used as it does modify the workarea itself.
-- a manual padding has to be generated.
local function get_margins(w)
local position = w.position
assert(position)
local margins = {left=0, right=0, top=0, bottom=0}
margins[position] = get_margin(w, position, true)
-- Avoid overlapping wibars
if position == "left" or position == "right" then
margins.top = get_margin(w, "top" )
margins.bottom = get_margin(w, "bottom")
end
return margins
end
-- Create the placement function
local function gen_placement(position, stretch)
local maximize = (position == "right" or position == "left") and
"maximize_vertically" or "maximize_horizontally"
return placement[position] + (stretch and placement[maximize] or nil)
end
-- Attach the placement function.
local function attach(wb, align)
gen_placement(align, wb._stretch)(wb, {
attach = true,
update_workarea = true,
margins = get_margins(wb)
})
end
-- Re-attach all wibars on a given wibar screen
local function reattach(wb)
local s = wb.screen
for _, w in ipairs(wiboxes) do
if w ~= wb and w.screen == s then
if w.detach_callback then
w.detach_callback()
w.detach_callback = nil
end
attach(w, w.position)
end
end
end
--- The wibox position.
-- @property position
-- @param string Either "left", right", "top" or "bottom"
local function get_position(wb)
return wb._position or "top"
end
local function set_position(wb, position, skip_reattach)
-- Detach first to avoid any uneeded callbacks
if wb.detach_callback then
wb.detach_callback()
-- Avoid disconnecting twice, this produces a lot of warnings
wb.detach_callback = nil
end
-- Move the wibar to the end of the list to avoid messing up the others in
-- case there is stacked wibars on one side.
if wb._position then
for k, w in ipairs(wiboxes) do
if w == wb then
table.remove(wiboxes, k)
end
end
table.insert(wiboxes, wb)
end
-- In case the position changed, it may be necessary to reset the size
if (wb._position == "left" or wb._position == "right")
and (position == "top" or position == "bottom") then
wb.height = math.ceil(beautiful.get_font_height(wb.font) * 1.5)
elseif (wb._position == "top" or wb._position == "bottom")
and (position == "left" or position == "right") then
wb.width = math.ceil(beautiful.get_font_height(wb.font) * 1.5)
end
-- Set the new position
wb._position = position
-- Attach to the new position
attach(wb, position)
-- A way to skip reattach is required when first adding a wibar as it's not
-- in the `wiboxes` table yet and can't be added until it's attached.
if not skip_reattach then
-- Changing the position will also cause the other margins to be invalidated.
-- For example, adding a wibar to the top will change the margins of any left
-- or right wibars. To solve, this, they need to be re-attached.
reattach(wb)
end
end
--- Stretch the wibar.
--
-- @property stretch
-- @param[opt=true] boolean
local function get_stretch(w)
return w._stretch
end
local function set_stretch(w, value)
w._stretch = value
attach(w, w.position)
end
--- Remove a wibar.
-- @function remove
local function remove(self)
self.visible = false
if self.detach_callback then
self.detach_callback()
self.detach_callback = nil
end
for k, w in ipairs(wiboxes) do
if w == self then
table.remove(wiboxes, k)
end
end
self._screen = nil
end
--- Get a wibox position if it has been set, or return top.
-- @param wb The wibox
-- @deprecated awful.wibar.get_position
-- @return The wibox position.
function awfulwibar.get_position(wb)
gdebug.deprecate("Use wb:get_position() instead of awful.wibar.get_position", {deprecated_in=4})
return get_position(wb)
end
--- Put a wibox on a screen at this position.
-- @param wb The wibox to attach.
-- @param position The position: top, bottom left or right.
-- @param screen This argument is deprecated, use wb.screen directly.
-- @deprecated awful.wibar.set_position
function awfulwibar.set_position(wb, position, screen) --luacheck: no unused args
gdebug.deprecate("Use wb:set_position(position) instead of awful.wibar.set_position", {deprecated_in=4})
set_position(wb, position)
end
--- Attach a wibox to a screen.
--
-- This function has been moved to the `awful.placement` module. Calling this
-- no longer does anything.
--
-- @param wb The wibox to attach.
-- @param position The position of the wibox: top, bottom, left or right.
-- @param screen The screen to attach to
-- @see awful.placement
-- @deprecated awful.wibar.attach
function awfulwibar.attach(wb, position, screen) --luacheck: no unused args
gdebug.deprecate("awful.wibar.attach is deprecated, use the 'attach' property"..
" of awful.placement. This method doesn't do anything anymore",
{deprecated_in=4}
)
end
--- Align a wibox.
--
-- Supported alignment are:
--
-- * top_left
-- * top_right
-- * bottom_left
-- * bottom_right
-- * left
-- * right
-- * top
-- * bottom
-- * centered
-- * center_vertical
-- * center_horizontal
--
-- @param wb The wibox.
-- @param align The alignment
-- @param screen This argument is deprecated. It is not used. Use wb.screen
-- directly.
-- @deprecated awful.wibar.align
-- @see awful.placement.align
function awfulwibar.align(wb, align, screen) --luacheck: no unused args
if align == "center" then
gdebug.deprecate("awful.wibar.align(wb, 'center' is deprecated, use 'centered'", {deprecated_in=4})
align = "centered"
end
if screen then
gdebug.deprecate("awful.wibar.align 'screen' argument is deprecated", {deprecated_in=4})
end
if placement[align] then
return placement[align](wb)
end
end
--- Stretch a wibox so it takes all screen width or height.
--
-- **This function has been removed.**
--
-- @deprecated awful.wibox.stretch
-- @see awful.placement
-- @see awful.wibar.stretch
--- Create a new wibox and attach it to a screen edge.
-- 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, the primary screen is assumed.
-- @see wibox
-- @tparam[opt=nil] table arg
-- @tparam string arg.position The position.
-- @tparam string arg.stretch If the wibar need to be stretched to fill the screen.
-- @tparam integer arg.border_width Border width.
-- @tparam string arg.border_color Border color.
-- @tparam boolean arg.ontop On top of other windows.
-- @tparam string arg.cursor The mouse cursor.
-- @tparam boolean arg.visible Visibility.
-- @tparam number arg.opacity The wibar's opacity, between 0 and 1.
-- @tparam string arg.type The window type (desktop, normal, dock, …).
-- @tparam integer arg.x The x coordinates.
-- @tparam integer arg.y The y coordinates.
-- @tparam integer arg.width The wibar's width.
-- @tparam integer arg.height The wibar's height.
-- @tparam screen arg.screen The wibox screen.
-- @tparam wibox.widget arg.widget The widget that the wibox displays.
-- @param arg.shape_bounding The wiboxs bounding shape as a (native) cairo surface.
-- @param arg.shape_clip The wiboxs clip shape as a (native) cairo surface.
-- @param arg.shape_input The wiboxs input shape as a (native) cairo surface.
-- @tparam color arg.bg The wibar's background.
-- @tparam surface arg.bgimage The background image of the drawable.
-- @tparam color arg.fg The wibar's foreground (text) color.
-- @return The new wibar
-- @function awful.wibar
function awfulwibar.new(arg)
arg = arg or {}
local position = arg.position or "top"
local has_to_stretch = true
local screen = get_screen(arg.screen or 1)
arg.type = arg.type or "dock"
if position ~= "top" and position ~="bottom"
and position ~= "left" and position ~= "right" then
error("Invalid position in awful.wibar(), 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 math.ceil(beautiful.get_font_height(arg.font) * 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 = math.ceil(screen.geometry.height * hp / 100)
end
end
end
else
arg.height = arg.height or math.ceil(beautiful.get_font_height(arg.font) * 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 = math.ceil(screen.geometry.width * wp / 100)
end
end
end
end
arg.screen = nil
-- The C code scans the table directly, so metatable magic cannot be used.
for _, prop in ipairs {
"border_width", "border_color", "font", "opacity", "ontop", "cursor",
"height", "width", "bgimage", "bg", "fg", "type", "stretch", "shape"
} do
if (arg[prop] == nil) and beautiful["wibar_"..prop] ~= nil then
arg[prop] = beautiful["wibar_"..prop]
end
end
local w = wibox(arg)
w.screen = screen
w._screen = screen --HACK When a screen is removed, then getbycoords wont work
w._stretch = arg.stretch == nil and has_to_stretch or arg.stretch
w.get_position = get_position
w.set_position = set_position
w.get_stretch = get_stretch
w.set_stretch = set_stretch
w.remove = remove
if arg.visible == nil then w.visible = true end
-- `w` needs to be inserted in `wiboxes` before reattach or its own offset
-- will not be taken into account by the "older" wibars when `reattach` is
-- called. `skip_reattach` is required.
w:set_position(position, true)
table.insert(wiboxes, w)
-- Force all the wibars to be moved
reattach(w)
w:connect_signal("property::visible", function() reattach(w) end)
return w
end
capi.screen.connect_signal("removed", function(s)
local wibars = {}
for _, wibar in ipairs(wiboxes) do
if wibar._screen == s then
table.insert(wibars, wibar)
end
end
for _, wibar in ipairs(wibars) do
wibar:remove()
end
end)
function awfulwibar.mt:__call(...)
return awfulwibar.new(...)
end
--@DOC_wibox_COMMON@
return setmetatable(awfulwibar, awfulwibar.mt)
-- vim: filetype=lua:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:textwidth=80