Merge pull request #878 from Elv13/geometry_overhaul_p3.02

Geometry overhaul part 3.02: Wibars
This commit is contained in:
Emmanuel Lepage Vallée 2016-05-16 14:13:38 -04:00
commit 32eeaa9513
23 changed files with 1479 additions and 830 deletions

View File

@ -196,7 +196,7 @@ awful.screen.connect_for_each_screen(function(s)
mytasklist[s] = awful.widget.tasklist(s, awful.widget.tasklist.filter.currenttags, mytasklist.buttons) mytasklist[s] = awful.widget.tasklist(s, awful.widget.tasklist.filter.currenttags, mytasklist.buttons)
-- Create the wibox -- Create the wibox
mywibox[s] = awful.wibox({ position = "top", screen = s }) mywibox[s] = awful.wibar({ position = "top", screen = s })
-- Add widgets to the wibox -- Add widgets to the wibox
mywibox[s]:setup { mywibox[s]:setup {

224
docs/common/wibox.ldoc Normal file
View File

@ -0,0 +1,224 @@
--- Border width.
--
-- **Signal:**
--
-- * *property::border_width*
--
-- @property border_width
-- @param integer
--- Border color.
--
-- Please note that this property only support string based 24 bit or 32 bit
-- colors:
--
-- Red Blue
-- _| _|
-- #FF00FF
-- T‾
-- Green
--
--
-- Red Blue
-- _| _|
-- #FF00FF00
-- T‾ ‾T
-- Green Alpha
--
-- **Signal:**
--
-- * *property::border_color*
--
-- @property border_color
-- @param string
--- On top of other windows.
--
-- **Signal:**
--
-- * *property::ontop*
--
-- @property ontop
-- @param boolean
--- The mouse cursor.
--
-- **Signal:**
--
-- * *property::cursor*
--
-- @property cursor
-- @param string
-- @see mouse
--- Visibility.
--
-- **Signal:**
--
-- * *property::visible*
--
-- @property visible
-- @param boolean
--- The opacity of the wibox, between 0 and 1.
--
-- **Signal:**
--
-- * *property::opacity*
--
-- @property opacity
-- @tparam number opacity (between 0 and 1)
--- The window type (desktop, normal, dock, ...).
--
-- **Signal:**
--
-- * *property::type*
--
-- @property type
-- @param string
-- @see client.type
--- The x coordinates.
--
-- **Signal:**
--
-- * *property::x*
--
-- @property x
-- @param integer
--- The y coordinates.
--
-- **Signal:**
--
-- * *property::y*
--
-- @property y
-- @param integer
--- The width of the wibox.
--
-- **Signal:**
--
-- * *property::width*
--
-- @property width
-- @param width
--- The height of the wibox.
--
-- **Signal:**
--
-- * *property::height*
--
-- @property height
-- @param height
--- The wibox screen.
--
-- @property screen
-- @param screen
--- The wibox's `drawable`.
--
-- **Signal:**
--
-- * *property::drawable*
--
-- @property drawable
-- @tparam drawable drawable
--- The widget that the `wibox` displays.
-- @property widget
-- @param widget
--- The X window id.
--
-- **Signal:**
--
-- * *property::window*
--
-- @property window
-- @param string
-- @see client.window
--- The wibox's bounding shape as a (native) cairo surface.
--
-- **Signal:**
--
-- * *property::shape_bounding*
--
-- @property shape_bounding
-- @param surface._native
--- The wibox's clip shape as a (native) cairo surface.
--
-- **Signal:**
--
-- * *property::shape_clip*
--
-- @property shape_clip
-- @param surface._native
--- Get or set mouse buttons bindings to a wibox.
--
-- @param buttons_table A table of buttons objects, or nothing.
-- @function wibox.buttons
--- Get or set wibox geometry. That's the same as accessing or setting the x,
-- y, width or height properties of a wibox.
--
-- @param A table with coordinates to modify.
-- @return A table with wibox coordinates and geometry.
-- @function wibox.geometry
--- Get or set wibox struts.
--
-- @param strut A table with new strut, or nothing
-- @return The wibox strut in a table.
-- @function wibox.struts
-- @see client.struts
--- The default background color.
-- @beautiful beautiful.bg_normal
-- @see bg
--- The default foreground (text) color.
-- @beautiful beautiful.fg_normal
-- @see fg
--- Set a declarative widget hierarchy description.
-- See [The declarative layout system](../documentation/03-declarative-layout.md.html)
-- @param args An array containing the widgets disposition
-- @name setup
-- @class function
--- The background of the wibox.
-- @param c The background to use. This must either be a cairo pattern object,
-- nil or a string that gears.color() understands.
-- @property bg
-- @see gears.color
--- The background image of the drawable.
-- If `image` is a function, it will be called with `(context, cr, width, height)`
-- as arguments. Any other arguments passed to this method will be appended.
-- @param image A background image or a function
-- @property bgimage
-- @see gears.surface
--- The foreground (text) of the wibox.
-- @param c The foreground to use. This must either be a cairo pattern object,
-- nil or a string that gears.color() understands.
-- @property fg
-- @see gears.color
--- Find a widget by a point.
-- The wibox must have drawn itself at least once for this to work.
-- @tparam number x X coordinate of the point
-- @tparam number y Y coordinate of the point
-- @treturn table A sorted table of widgets positions. The first element is the biggest
-- container while the last is the topmost widget. The table contains *x*, *y*,
-- *width*, *height* and *widget*.
-- @name find_widgets
-- @class function

View File

@ -11,6 +11,7 @@
local util = require("awful.util") local util = require("awful.util")
local spawn = require("awful.spawn") local spawn = require("awful.spawn")
local object = require("gears.object") local object = require("gears.object")
local grect = require("gears.geometry").rectangle
local pairs = pairs local pairs = pairs
local type = type local type = type
local ipairs = ipairs local ipairs = ipairs
@ -197,7 +198,7 @@ function client.swap.bydirection(dir, c, stacked)
for i,cl in ipairs(cltbl) do for i,cl in ipairs(cltbl) do
geomtbl[i] = cl:geometry() geomtbl[i] = cl:geometry()
end end
local target = util.get_rectangle_in_direction(dir, geomtbl, sel:geometry()) local target = grect.get_in_direction(dir, geomtbl, sel:geometry())
-- If we found a client to swap with, then go for it -- If we found a client to swap with, then go for it
if target then if target then
@ -693,6 +694,54 @@ function client.floating.delete(c)
client.object.set_floating(c, nil) client.object.set_floating(c, nil)
end end
--- The x coordinates.
--
-- **Signal:**
--
-- * *property::x*
--
-- @property x
-- @param integer
--- The y coordinates.
--
-- **Signal:**
--
-- * *property::y*
--
-- @property y
-- @param integer
--- The width of the wibox.
--
-- **Signal:**
--
-- * *property::width*
--
-- @property width
-- @param width
--- The height of the wibox.
--
-- **Signal:**
--
-- * *property::height*
--
-- @property height
-- @param height
-- Add the geometry helpers to match the wibox API
for _, v in ipairs {"x", "y", "width", "height"} do
client.object["get_"..v] = function(c)
return c:geometry()[v]
end
client.object["set_"..v] = function(c, value)
return c:geometry({[v] = value})
end
end
--- Restore (=unminimize) a random client. --- Restore (=unminimize) a random client.
-- @function awful.client.restore -- @function awful.client.restore
-- @param s The screen to use. -- @param s The screen to use.

View File

@ -6,7 +6,7 @@
-- @release @AWESOME_VERSION@ -- @release @AWESOME_VERSION@
-- @submodule client -- @submodule client
--------------------------------------------------------------------------- ---------------------------------------------------------------------------
local util = require("awful.util") local grect = require("gears.geometry").rectangle
local capi = local capi =
{ {
@ -168,7 +168,7 @@ function focus.bydirection(dir, c, stacked)
geomtbl[i] = cl:geometry() geomtbl[i] = cl:geometry()
end end
local target = util.get_rectangle_in_direction(dir, geomtbl, sel:geometry()) local target = grect.get_in_direction(dir, geomtbl, sel:geometry())
-- If we found a client to focus, then do it. -- If we found a client to focus, then do it.
if target then if target then
@ -200,7 +200,7 @@ function focus.global_bydirection(dir, c, stacked)
for i,cl in ipairs(cltbl) do for i,cl in ipairs(cltbl) do
geomtbl[i] = cl:geometry() geomtbl[i] = cl:geometry()
end end
local target = util.get_rectangle_in_direction(dir, geomtbl, scr.geometry) local target = grect.get_in_direction(dir, geomtbl, scr.geometry)
if target then if target then
cltbl[target]:emit_signal("request::activate", cltbl[target]:emit_signal("request::activate",

View File

@ -51,6 +51,7 @@ return
remote = require("awful.remote"); remote = require("awful.remote");
key = require("awful.key"); key = require("awful.key");
button = require("awful.button"); button = require("awful.button");
wibar = require("awful.wibar");
wibox = require("awful.wibox"); wibox = require("awful.wibox");
startup_notification = require("awful.startup_notification"); startup_notification = require("awful.startup_notification");
tooltip = require("awful.tooltip"); tooltip = require("awful.tooltip");

View File

@ -10,7 +10,6 @@
-- Grab environment we need -- Grab environment we need
local layout = require("awful.layout") local layout = require("awful.layout")
local aplace = require("awful.placement") local aplace = require("awful.placement")
local awibox = require("awful.wibox")
local util = require("awful.util") local util = require("awful.util")
local type = type local type = type
local ipairs = ipairs local ipairs = ipairs
@ -123,43 +122,31 @@ end
--- Move the wibox under the cursor. --- Move the wibox under the cursor.
-- @function awful.mouse.wibox.move -- @function awful.mouse.wibox.move
--@param w The wibox to move, or none to use that under the pointer --@tparam wibox w The wibox to move, or none to use that under the pointer
function mouse.wibox.move(w) function mouse.wibox.move(w)
w = w or mouse.wibox_under_pointer() w = w or mouse.wibox_under_pointer()
if not w then return end if not w then return end
if not w
or w.type == "desktop"
or w.type == "splash"
or w.type == "dock" then
return
end
-- Compute the offset
local coords = capi.mouse.coords()
local geo = aplace.centered(capi.mouse,{parent=w, pretend=true})
local offset = { local offset = {
x = w.x - capi.mouse.coords().x, x = geo.x - coords.x,
y = w.y - capi.mouse.coords().y y = geo.y - coords.y,
} }
capi.mousegrabber.run(function (_mouse) mouse.resize(w, "mouse.move", {
local button_down = false placement = aplace.under_mouse,
if awibox.get_position(w) == "floating" then offset = offset
w.x = capi.mouse.coords().x + offset.x })
w.y = capi.mouse.coords().y + offset.y
else
local wa = capi.screen[capi.mouse.screen].workarea
if capi.mouse.coords()["y"] > wa.y + wa.height - 10 then
awibox.set_position(w, "bottom", w.screen)
elseif capi.mouse.coords()["y"] < wa.y + 10 then
awibox.set_position(w, "top", w.screen)
elseif capi.mouse.coords()["x"] > wa.x + wa.width - 10 then
awibox.set_position(w, "right", w.screen)
elseif capi.mouse.coords()["x"] < wa.x + 10 then
awibox.set_position(w, "left", w.screen)
end
w.screen = capi.mouse.screen
end
for _, v in ipairs(_mouse.buttons) do
if v then button_down = true end
end
if not button_down then
return false
end
return true
end, "fleur")
end end
--- Get a client corner coordinates. --- Get a client corner coordinates.
@ -234,22 +221,11 @@ function mouse.resize_handler(c, context, hints)
local lay = c.screen.selected_tag.layout local lay = c.screen.selected_tag.layout
if lay == layout.suit.floating or c.floating then if lay == layout.suit.floating or c.floating then
local offset = hints and hints.offset or {}
if type(offset) == "number" then
offset = {
x = offset,
y = offset,
width = offset,
height = offset,
}
end
c:geometry { c:geometry {
x = hints.x + (offset.x or 0 ), x = hints.x,
y = hints.y + (offset.y or 0 ), y = hints.y,
width = hints.width + (offset.width or 0 ), width = hints.width,
height = hints.height + (offset.height or 0 ), height = hints.height,
} }
elseif lay.resize_handler then elseif lay.resize_handler then
lay.resize_handler(c, context, hints) lay.resize_handler(c, context, hints)

View File

@ -51,6 +51,11 @@
-- --
-- **attach** (*boolean*): -- **attach** (*boolean*):
-- --
-- When the parent geometry (like the screen) changes, re-apply the placement
-- function. This will add a `detach_callback` function to the drawable. Call
-- this to detach the function. This will be called automatically when a new
-- attached function is set.
--
-- **offset** (*table or number*): -- **offset** (*table or number*):
-- --
-- The offset(s) to apply to the new geometry. -- The offset(s) to apply to the new geometry.
@ -88,6 +93,7 @@ local capi =
local client = require("awful.client") local client = require("awful.client")
local layout = require("awful.layout") local layout = require("awful.layout")
local a_screen = require("awful.screen") local a_screen = require("awful.screen")
local grect = require("gears.geometry").rectangle
local util = require("awful.util") local util = require("awful.util")
local dpi = require("beautiful").xresources.apply_dpi local dpi = require("beautiful").xresources.apply_dpi
@ -96,6 +102,15 @@ local function get_screen(s)
end end
local wrap_client = nil local wrap_client = nil
local placement
-- Store function -> keys
local reverse_align_map = {}
-- Forward declarations
local area_common
local wibox_update_strut
local attach
--- Allow multiple placement functions to be daisy chained. --- Allow multiple placement functions to be daisy chained.
-- This also allow the functions to be aware they are being chained and act -- This also allow the functions to be aware they are being chained and act
@ -123,7 +138,8 @@ local function compose(...)
end end
end end
local ret = wrap_client(function(d, args, ...) local ret
ret = wrap_client(function(d, args, ...)
local rets = {} local rets = {}
local last_geo = nil local last_geo = nil
@ -134,13 +150,16 @@ local function compose(...)
-- Only apply the geometry once, not once per chain node, to do this, -- Only apply the geometry once, not once per chain node, to do this,
-- Force the "pretend" argument and restore the original value for -- Force the "pretend" argument and restore the original value for
-- the last node. -- the last node.
local pretend_real = args.pretend local attach_real = args.attach
args.pretend = true args.pretend = true
args.attach = false
args.offset = {}
for k, f in ipairs(queue) do for k, f in ipairs(queue) do
if k == #queue then if k == #queue then
args.pretend = pretend_real or false -- Let them fallback to the parent table
args.pretend = nil
args.offset = nil
end end
local r = {f(d, args, ...)} local r = {f(d, args, ...)}
@ -160,6 +179,11 @@ local function compose(...)
end end
end end
if attach_real then
args.attach = true
attach(d, ret, args)
end
return last_geo, rets return last_geo, rets
end, "compose") end, "compose")
@ -189,7 +213,7 @@ local placement_private = {}
-- --
-- (awful.placement.no_overlap + awful.placement.no_offscreen)(c) -- (awful.placement.no_overlap + awful.placement.no_offscreen)(c)
-- --
local placement = setmetatable({}, { placement = setmetatable({}, {
__index = placement_private, __index = placement_private,
__newindex = function(_, k, f) __newindex = function(_, k, f)
placement_private[k] = wrap_client(f, k) placement_private[k] = wrap_client(f, k)
@ -221,9 +245,6 @@ local align_map = {
center_horizontal = function(sw, _ , dw, _ ) return {x=sw/2-dw/2, y= nil } end, center_horizontal = function(sw, _ , dw, _ ) return {x=sw/2-dw/2, y= nil } end,
} }
-- Store function -> keys
local reverse_align_map = {}
-- Some parameters to correctly compute the final size -- Some parameters to correctly compute the final size
local resize_to_point_map = { local resize_to_point_map = {
-- Corners -- Corners
@ -259,39 +280,61 @@ local function store_geometry(d, reqtype)
data[d][reqtype].screen = d.screen data[d][reqtype].screen = d.screen
end end
--- Apply some modifications before applying the new geometry. --- Get the margins and offset
-- @tparam table new_geo The new geometry -- @tparam table args The arguments
-- @tparam table args The common arguments -- @treturn table The margins
-- @treturn table|nil The new geometry -- @treturn table The offsets
local function fix_new_geometry(new_geo, args) local function get_decoration(args)
if args.pretend or not new_geo then return nil end local offset = args.offset
local offset = args.offset or {} -- Offset are "blind" values added to the output
offset = type(offset) == "number" and {
if type(offset) == "number" then
offset = {
x = offset, x = offset,
y = offset, y = offset,
width = offset, width = offset,
height = offset, height = offset,
} or args.offset or {}
-- Margins are distances on each side to substract from the area`
local m = type(args.margins) == "table" and args.margins or {
left = args.margins or 0 , right = args.margins or 0,
top = args.margins or 0 , bottom = args.margins or 0
} }
return m, offset
end end
--- Apply some modifications before applying the new geometry.
-- @tparam table new_geo The new geometry
-- @tparam table args The common arguments
-- @tparam boolean force Always ajust the geometry, even in pretent mode. This
-- should only be used when returning the final geometry as it would otherwise
-- mess the pipeline.
-- @treturn table|nil The new geometry
local function fix_new_geometry(new_geo, args, force)
if (args.pretend and not force) or not new_geo then return nil end
local m, offset = get_decoration(args)
return { return {
x = new_geo.x and (new_geo.x + (offset.x or 0)), x = new_geo.x and (new_geo.x + (offset.x or 0) + (m.left or 0) ),
y = new_geo.y and (new_geo.y + (offset.y or 0)), y = new_geo.y and (new_geo.y + (offset.y or 0) + (m.top or 0) ),
width = new_geo.width and (new_geo.width + (offset.width or 0)), width = new_geo.width and math.max(
height = new_geo.height and (new_geo.height + (offset.height or 0)), 1, (new_geo.width + (offset.width or 0) - (m.left or 0) - (m.right or 0) )
),
height = new_geo.height and math.max(
1, (new_geo.height + (offset.height or 0) - (m.top or 0) - (m.bottom or 0) )
),
} }
end end
--- Get the area covered by a drawin. -- Get the area covered by a drawin.
-- @param d The drawin -- @param d The drawin
-- @tparam[opt=nil] table new_geo A new geometry -- @tparam[opt=nil] table new_geo A new geometry
-- @tparam[opt=false] boolean ignore_border_width Ignore the border -- @tparam[opt=false] boolean ignore_border_width Ignore the border
-- @tparam table args the method arguments -- @tparam table args the method arguments
-- @treturn The drawin's area. -- @treturn The drawin's area.
local function area_common(d, new_geo, ignore_border_width, args) area_common = function(d, new_geo, ignore_border_width, args)
-- The C side expect no arguments, nil isn't valid -- The C side expect no arguments, nil isn't valid
local geometry = new_geo and d:geometry(new_geo) or d:geometry() local geometry = new_geo and d:geometry(new_geo) or d:geometry()
local border = ignore_border_width and 0 or d.border_width or 0 local border = ignore_border_width and 0 or d.border_width or 0
@ -316,7 +359,6 @@ end
-- @tparam[opt=false] boolean ignore_border_width Ignore the border -- @tparam[opt=false] boolean ignore_border_width Ignore the border
-- @treturn table A table with *x*, *y*, *width* and *height*. -- @treturn table A table with *x*, *y*, *width* and *height*.
local function geometry_common(obj, args, new_geo, ignore_border_width) local function geometry_common(obj, args, new_geo, ignore_border_width)
-- Store the current geometry in a singleton-memento -- Store the current geometry in a singleton-memento
if args.store_geometry and new_geo and args.context then if args.store_geometry and new_geo and args.context then
store_geometry(obj, args.context) store_geometry(obj, args.context)
@ -338,16 +380,13 @@ local function geometry_common(obj, args, new_geo, ignore_border_width)
-- Apply the margins -- Apply the margins
if args.margins then if args.margins then
local delta = type(args.margins) == "table" and args.margins or { local delta = get_decoration(args)
left = args.margins , right = args.margins,
top = args.margins , bottom = args.margins
}
return { return {
x = dgeo.x + (delta.left or 0), x = dgeo.x - (delta.left or 0),
y = dgeo.y + (delta.top or 0), y = dgeo.y - (delta.top or 0),
width = dgeo.width - (delta.left or 0) - (delta.right or 0), width = dgeo.width + (delta.left or 0) + (delta.right or 0),
height = dgeo.height - (delta.top or 0) - (delta.bottom or 0), height = dgeo.height + (delta.top or 0) + (delta.bottom or 0),
} }
end end
@ -367,12 +406,18 @@ end
-- @tparam table args the method arguments -- @tparam table args the method arguments
-- @treturn table A table with *x*, *y*, *width* and *height*. -- @treturn table A table with *x*, *y*, *width* and *height*.
local function get_parent_geometry(obj, args) local function get_parent_geometry(obj, args)
-- Didable override_geometry, context and other to avoid mutating the state
-- or using the wrong geo.
if args.bounding_rect then if args.bounding_rect then
return args.bounding_rect return args.bounding_rect
elseif args.parent then elseif args.parent then
return geometry_common(args.parent, args) return geometry_common(args.parent, {})
elseif obj.screen then elseif obj.screen then
return geometry_common(obj.screen, args) return geometry_common(obj.screen, {
honor_padding = args.honor_padding,
honor_workarea = args.honor_workarea
})
else else
return geometry_common(capi.screen[capi.mouse.screen], args) return geometry_common(capi.screen[capi.mouse.screen], args)
end end
@ -405,7 +450,7 @@ local function move_into_geometry(source, target)
end end
-- Update the workarea -- Update the workarea
local function wibox_update_strut(d, position) wibox_update_strut = function(d, position, args)
-- If the drawable isn't visible, remove the struts -- If the drawable isn't visible, remove the struts
if not d.visible then if not d.visible then
d:struts { left = 0, right = 0, bottom = 0, top = 0 } d:struts { left = 0, right = 0, bottom = 0, top = 0 }
@ -420,16 +465,18 @@ local function wibox_update_strut(d, position)
-- the workarea -- the workarea
local struts = { left = 0, right = 0, bottom = 0, top = 0 } local struts = { left = 0, right = 0, bottom = 0, top = 0 }
local m = get_decoration(args)
if vertical then if vertical then
for _, v in ipairs {"right", "left"} do for _, v in ipairs {"right", "left"} do
if (not position) or position:match(v) then if (not position) or position:match(v) then
struts[v] = geo.width struts[v] = geo.width + m[v]
end end
end end
else else
for _, v in ipairs {"top", "bottom"} do for _, v in ipairs {"top", "bottom"} do
if (not position) or position:match(v) then if (not position) or position:match(v) then
struts[v] = geo.height struts[v] = geo.height + m[v]
end end
end end
end end
@ -438,16 +485,18 @@ local function wibox_update_strut(d, position)
d:struts(struts) d:struts(struts)
end end
--- Pin a drawable to a placement function. -- Pin a drawable to a placement function.
-- Automatically update the position when the size change. -- Automatically update the position when the size change.
-- All other arguments will be passed to the `position` function (if any) -- All other arguments will be passed to the `position` function (if any)
-- @tparam[opt=client.focus] drawable d A drawable (like `client`, `mouse` -- @tparam[opt=client.focus] drawable d A drawable (like `client`, `mouse`
-- or `wibox`) -- or `wibox`)
-- @param position_f A position name (see `align`) or a position function -- @param position_f A position name (see `align`) or a position function
-- @tparam[opt={}] table args Other arguments -- @tparam[opt={}] table args Other arguments
local function attach(d, position_f, args) attach = function(d, position_f, args)
args = args or {} args = args or {}
if args.pretend then return end
if not args.attach then return end if not args.attach then return end
-- Avoid a connection loop -- Avoid a connection loop
@ -462,110 +511,78 @@ local function attach(d, position_f, args)
if not position_f then return end if not position_f then return end
-- If there is multiple attached function, there is an high risk of infinite
-- loop. While some combinaisons are harmless, other are very hard to debug.
--
-- Use the placement composition to build explicit multi step attached
-- placement functions.
if d.detach_callback then
d.detach_callback()
d.detach_callback = nil
end
local function tracker() local function tracker()
position_f(d, args) position_f(d, args)
end end
d:connect_signal("property::width" , tracker) d:connect_signal("property::width" , tracker)
d:connect_signal("property::height" , tracker) d:connect_signal("property::height" , tracker)
d:connect_signal("property::border_width", tracker)
tracker()
if args.update_workarea then
local function tracker_struts() local function tracker_struts()
--TODO this is too fragile and doesn't work with all methods. --TODO this is too fragile and doesn't work with all methods.
wibox_update_strut(d, reverse_align_map[position_f]) wibox_update_strut(d, d.position or reverse_align_map[position_f], args)
end end
local parent = args.parent or d.screen
if args.update_workarea then
d:connect_signal("property::geometry" , tracker_struts) d:connect_signal("property::geometry" , tracker_struts)
d:connect_signal("property::visible" , tracker_struts) d:connect_signal("property::visible" , tracker_struts)
capi.client.connect_signal("property::struts", tracker_struts)
tracker_struts() tracker_struts()
elseif parent == d.screen then
if args.honor_workarea then
parent:connect_signal("property::workarea", tracker)
end
if args.honor_padding then
parent:connect_signal("property::padding", tracker)
end
end end
-- If there is a parent drawable, screen or mouse, also track it -- If there is a parent drawable, screen or mouse, also track it
local parent = args.parent or d.screen
if parent then if parent then
args.parent:connect_signal("property::geometry" , tracker) parent:connect_signal("property::geometry" , tracker)
end
end end
--- Check if an area intersect another area. -- Create a way to detach a placement function
-- @param a The area. d:add_signal("property::detach_callback")
-- @param b The other area. function d.detach_callback()
-- @return True if they intersect, false otherwise. d:disconnect_signal("property::width" , tracker)
local function area_intersect_area(a, b) d:disconnect_signal("property::height" , tracker)
return (b.x < a.x + a.width d:disconnect_signal("property::border_width", tracker)
and b.x + b.width > a.x if parent then
and b.y < a.y + a.height parent:disconnect_signal("property::geometry" , tracker)
and b.y + b.height > a.y)
if parent == d.screen then
if args.honor_workarea then
parent:disconnect_signal("property::workarea", tracker)
end end
--- Get the intersect area between a and b. if args.honor_padding then
-- @param a The area. parent:disconnect_signal("property::padding", tracker)
-- @param b The other area.
-- @return The intersect area.
local function area_intersect_area_get(a, b)
local g = {}
g.x = math.max(a.x, b.x)
g.y = math.max(a.y, b.y)
g.width = math.min(a.x + a.width, b.x + b.width) - g.x
g.height = math.min(a.y + a.height, b.y + b.height) - g.y
return g
end
--- Remove an area from a list, splitting the space between several area that
-- can overlap.
-- @param areas Table of areas.
-- @param elem Area to remove.
-- @return The new area list.
local function area_remove(areas, elem)
for i = #areas, 1, -1 do
-- Check if the 'elem' intersect
if area_intersect_area(areas[i], elem) then
-- It does? remove it
local r = table.remove(areas, i)
local inter = area_intersect_area_get(r, elem)
if inter.x > r.x then
table.insert(areas, {
x = r.x,
y = r.y,
width = inter.x - r.x,
height = r.height
})
end
if inter.y > r.y then
table.insert(areas, {
x = r.x,
y = r.y,
width = r.width,
height = inter.y - r.y
})
end
if inter.x + inter.width < r.x + r.width then
table.insert(areas, {
x = inter.x + inter.width,
y = r.y,
width = (r.x + r.width) - (inter.x + inter.width),
height = r.height
})
end
if inter.y + inter.height < r.y + r.height then
table.insert(areas, {
x = r.x,
y = inter.y + inter.height,
width = r.width,
height = (r.y + r.height) - (inter.y + inter.height)
})
end end
end end
end end
return areas if args.update_workarea then
d:disconnect_signal("property::geometry" , tracker_struts)
d:disconnect_signal("property::visible" , tracker_struts)
capi.client.disconnect_signal("property::struts", tracker_struts)
end
end
end end
-- Convert 2 points into a rectangle -- Convert 2 points into a rectangle
@ -633,7 +650,7 @@ function placement.closest_corner(d, args)
local new_args = setmetatable({position = corner}, {__index=args}) local new_args = setmetatable({position = corner}, {__index=args})
local ngeo = placement_private.align(d, new_args) local ngeo = placement_private.align(d, new_args)
return ngeo, corner return fix_new_geometry(ngeo, args, true), corner
end end
--- Place the client so no part of it will be outside the screen (workarea). --- Place the client so no part of it will be outside the screen (workarea).
@ -685,7 +702,7 @@ function placement.no_overlap(c)
local areas = { screen.workarea } local areas = { screen.workarea }
for _, cl in pairs(cls) do for _, cl in pairs(cls) do
if cl ~= c and cl.type ~= "desktop" and (cl.floating or curlay == layout.suit.floating) then if cl ~= c and cl.type ~= "desktop" and (cl.floating or curlay == layout.suit.floating) then
areas = area_remove(areas, area_common(cl)) areas = grect.area_remove(areas, area_common(cl))
end end
end end
@ -751,7 +768,9 @@ function placement.under_mouse(d, args)
ngeo.width = ngeo.width - 2*bw ngeo.width = ngeo.width - 2*bw
ngeo.height = ngeo.height - 2*bw ngeo.height = ngeo.height - 2*bw
return ngeo geometry_common(d, args, ngeo)
return fix_new_geometry(ngeo, args, true)
end end
--- Place the client next to the mouse. --- Place the client next to the mouse.
@ -862,7 +881,7 @@ function placement.resize_to_mouse(d, args)
geometry_common(d, args, ngeo) geometry_common(d, args, ngeo)
return ngeo return fix_new_geometry(ngeo, args, true)
end end
--- Move the drawable (client or wibox) `d` to a screen position or side. --- Move the drawable (client or wibox) `d` to a screen position or side.
@ -913,7 +932,7 @@ function placement.align(d, args)
attach(d, placement[args.position], args) attach(d, placement[args.position], args)
return ngeo return fix_new_geometry(ngeo, args, true)
end end
-- Add the alias functions -- Add the alias functions
@ -1002,7 +1021,7 @@ function placement.stretch(d, args)
attach(d, placement["stretch_"..args.direction], args) attach(d, placement["stretch_"..args.direction], args)
return ngeo return fix_new_geometry(ngeo, args, true)
end end
-- Add the alias functions -- Add the alias functions
@ -1056,7 +1075,7 @@ function placement.maximize(d, args)
attach(d, placement.maximize, args) attach(d, placement.maximize, args)
return ngeo return fix_new_geometry(ngeo, args, true)
end end
-- Add the alias functions -- Add the alias functions
@ -1121,7 +1140,7 @@ function placement.scale(d, args)
attach(d, placement.maximize, args) attach(d, placement.maximize, args)
return ngeo return fix_new_geometry(ngeo, args, true)
end end
---@DOC_awful_placement_maximize_vertically_EXAMPLE@ ---@DOC_awful_placement_maximize_vertically_EXAMPLE@

View File

@ -16,6 +16,7 @@ local capi =
} }
local util = require("awful.util") local util = require("awful.util")
local object = require("gears.object") local object = require("gears.object")
local grect = require("gears.geometry").rectangle
local function get_screen(s) local function get_screen(s)
return s and capi.screen[s] return s and capi.screen[s]
@ -62,20 +63,7 @@ end
-- @tparam number y Y coordinate of point -- @tparam number y Y coordinate of point
-- @treturn number The squared distance of the screen to the provided point -- @treturn number The squared distance of the screen to the provided point
function screen.object.get_square_distance(self, x, y) function screen.object.get_square_distance(self, x, y)
self = get_screen(self) return grect.get_square_distance(get_screen(self).geometry, x, y)
local geom = self.geometry
local dist_x, dist_y = 0, 0
if x < geom.x then
dist_x = geom.x - x
elseif x >= geom.x + geom.width then
dist_x = x - geom.x - geom.width + 1
end
if y < geom.y then
dist_y = geom.y - y
elseif y >= geom.y + geom.height then
dist_y = y - geom.y - geom.height + 1
end
return dist_x * dist_x + dist_y * dist_y
end end
--- ---
@ -83,20 +71,18 @@ end
-- The number returned can be used as an index into the global -- The number returned can be used as an index into the global
-- `screen` table/object. -- `screen` table/object.
-- @function awful.screen.getbycoord -- @function awful.screen.getbycoord
-- @param x The x coordinate -- @tparam number x The x coordinate
-- @param y The y coordinate -- @tparam number y The y coordinate
-- @treturn ?number The screen index
function screen.getbycoord(x, y) function screen.getbycoord(x, y)
local dist = math.huge local s, sgeos = capi.screen.primary, {}
local s = capi.screen.primary
if s then for scr in capi.screen do
dist = screen.object.get_square_distance(s, x, y) sgeos[scr] = scr.geometry
end
for i in capi.screen do
local d = screen.object.get_square_distance(i, x, y)
if d < dist then
s, dist = capi.screen[i], d
end
end end
s = grect.get_closest_by_coord(sgeos, x, y) or s
return s and s.index return s and s.index
end end
@ -151,7 +137,7 @@ function screen.focus_bydirection(dir, _screen)
for s in capi.screen do for s in capi.screen do
geomtbl[s] = capi.screen[s].geometry geomtbl[s] = capi.screen[s].geometry
end end
local target = util.get_rectangle_in_direction(dir, geomtbl, sel.geometry) local target = grect.get_in_direction(dir, geomtbl, sel.geometry)
if target then if target then
return screen.focus(target) return screen.focus(target)
end end

View File

@ -20,6 +20,7 @@ local type = type
local rtable = table local rtable = table
local string = string local string = string
local lgi = require("lgi") local lgi = require("lgi")
local grect = require("gears.geometry").rectangle
local Gio = require("lgi").Gio local Gio = require("lgi").Gio
local Pango = lgi.Pango local Pango = lgi.Pango
local capi = local capi =
@ -301,76 +302,18 @@ function util.subsets(set)
return ret return ret
end end
--- Return true whether rectangle B is in the right direction
-- compared to rectangle A.
-- @param dir The direction.
-- @param gA The geometric specification for rectangle A.
-- @param gB The geometric specification for rectangle B.
-- @return True if B is in the direction of A.
local function is_in_direction(dir, gA, gB)
if dir == "up" then
return gA.y > gB.y
elseif dir == "down" then
return gA.y < gB.y
elseif dir == "left" then
return gA.x > gB.x
elseif dir == "right" then
return gA.x < gB.x
end
return false
end
--- Calculate distance between two points.
-- i.e: if we want to move to the right, we will take the right border
-- of the currently focused screen and the left side of the checked screen.
-- @param dir The direction.
-- @param _gA The first rectangle.
-- @param _gB The second rectangle.
-- @return The distance between the screens.
local function calculate_distance(dir, _gA, _gB)
local gAx = _gA.x
local gAy = _gA.y
local gBx = _gB.x
local gBy = _gB.y
if dir == "up" then
gBy = _gB.y + _gB.height
elseif dir == "down" then
gAy = _gA.y + _gA.height
elseif dir == "left" then
gBx = _gB.x + _gB.width
elseif dir == "right" then
gAx = _gA.x + _gA.width
end
return math.sqrt(math.pow(gBx - gAx, 2) + math.pow(gBy - gAy, 2))
end
--- Get the nearest rectangle in the given direction. Every rectangle is specified as a table --- Get the nearest rectangle in the given direction. Every rectangle is specified as a table
-- with 'x', 'y', 'width', 'height' keys, the same as client or screen geometries. -- with 'x', 'y', 'width', 'height' keys, the same as client or screen geometries.
-- @deprecated awful.util.get_rectangle_in_direction
-- @param dir The direction, can be either "up", "down", "left" or "right". -- @param dir The direction, can be either "up", "down", "left" or "right".
-- @param recttbl A table of rectangle specifications. -- @param recttbl A table of rectangle specifications.
-- @param cur The current rectangle. -- @param cur The current rectangle.
-- @return The index for the rectangle in recttbl closer to cur in the given direction. nil if none found. -- @return The index for the rectangle in recttbl closer to cur in the given direction. nil if none found.
-- @see gears.geometry
function util.get_rectangle_in_direction(dir, recttbl, cur) function util.get_rectangle_in_direction(dir, recttbl, cur)
local dist, dist_min util.deprecate("gears.geometry.rectangle.get_in_direction")
local target = nil
-- We check each object return grect.get_in_direction(dir, recttbl, cur)
for i, rect in pairs(recttbl) do
-- Check geometry to see if object is located in the right direction.
if is_in_direction(dir, cur, rect) then
-- Calculate distance between current and checked object.
dist = calculate_distance(dir, cur, rect)
-- If distance is shorter then keep the object.
if not target or dist < dist_min then
target = i
dist_min = dist
end
end
end
return target
end end
--- Join all tables given as parameters. --- Join all tables given as parameters.

352
lib/awful/wibar.lua Normal file
View File

@ -0,0 +1,352 @@
---------------------------------------------------------------------------
--- 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 &lt;elv1313@gmail.com&gt;
-- @copyright 2016 Emmanuel Lepage Vallee
-- @release @AWESOME_VERSION@
-- @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 util = require("awful.util")
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"})
-- 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)
-- 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
-- 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)
-- Set the new position
wb._position = position
-- Attach to the new position
attach(wb, position)
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)
util.deprecate("Use wb:get_position() instead of awful.wibar.get_position")
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
util.deprecate("Use wb:set_position(position) instead of awful.wibar.set_position")
set_position(wb, position)
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 wb The wibox to attach.
-- @param position The position of the wibox: top, bottom, left or right.
-- @param screen The screen to attach to
-- @deprecated awful.wibar.attach
function awfulwibar.attach(wb, position, screen) --luacheck: no unused args
util.deprecate("awful.wibar.attach is deprecated, use the 'attach' property"..
" of awful.placement. This method doesn't do anything anymore"
)
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
util.deprecate("awful.wibar.align(wb, 'center' is deprecated, use 'centered'")
align = "centered"
end
if screen then
util.deprecate("awful.wibar.align 'screen' argument is deprecated")
end
attach(wb, align)
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 stretch
--- Create a new wibox and attach it to a screen edge.
-- @see wibox
-- @param arg 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 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
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:add_signal("property::position")
w.get_position = get_position
w.set_position = set_position
w:add_signal("property::stretch")
w.get_stretch = get_stretch
w.set_stretch = set_stretch
w.remove = remove
w.visible = true
w:set_position(position)
table.insert(wiboxes, w)
w:connect_signal("property::visible", function() reattach(w) end)
return w
end
capi.screen.connect_signal("removed", function(s)
for _, wibar in ipairs(wiboxes) do
if wibar._screen == s then
wibar:remove()
end
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

View File

@ -1,302 +1,32 @@
--------------------------------------------------------------------------- ---------------------------------------------------------------------------
--- Wibox module for awful. --- This module is deprecated and has been renamed `awful.wibar`
-- This module allows you to easily create wibox and attach them to the edge of
-- a screen.
-- --
-- @author Julien Danjou &lt;julien@danjou.info&gt; -- @author Emmanuel Lepage Vallee &lt;elv1313@gmail.com&gt;
-- @copyright 2009 Julien Danjou -- @copyright 2016 Emmanuel Lepage Vallee
-- @release @AWESOME_VERSION@ -- @release @AWESOME_VERSION@
-- @module awful.wibox -- @module awful.wibox
--------------------------------------------------------------------------- ---------------------------------------------------------------------------
local util = require("awful.util")
local wibar = require("awful.wibar")
-- Grab environment we need local function call(_,...)
local capi = util.deprecate("awful.wibox has been renamed to awful.wibar")
{
screen = screen,
client = client
}
local setmetatable = setmetatable
local tostring = tostring
local ipairs = ipairs
local table = table
local error = error
local wibox = require("wibox")
local beautiful = require("beautiful")
local round = require("awful.util").round
local function get_screen(s) return wibar(...)
return s and capi.screen[s]
end end
local awfulwibox = { mt = {} } local function index(_, k)
util.deprecate("awful.wibox has been renamed to awful.wibar")
--- Array of table with wiboxes inside. return wibar[k]
-- It's an array so it is ordered.
local wiboxes = {}
--- Get a wibox position if it has been set, or return top.
-- @param wb The wibox
-- @return The wibox position.
function awfulwibox.get_position(wb)
for _, wprop in ipairs(wiboxes) do
if wprop.wibox == wb then
return wprop.position
end
end
return "top"
end end
--- Put a wibox on a screen at this position. local function newindex(_, k, v)
-- @param wb The wibox to attach. util.deprecate("awful.wibox has been renamed to awful.wibar")
-- @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 awfulwibox.set_position(wb, position, screen)
local area = get_screen(screen).geometry
-- The "length" of a wibox is always chosen to be the optimal size wibar[k] = v
-- (non-floating).
-- The "width" of a wibox is kept if it exists.
if position == "right" then
wb.x = area.x + area.width - (wb.width + 2 * wb.border_width)
elseif position == "left" then
wb.x = area.x
elseif position == "bottom" then
wb.y = (area.y + area.height) - (wb.height + 2 * wb.border_width)
elseif position == "top" then
wb.y = area.y
end end
for _, wprop in ipairs(wiboxes) do return setmetatable({}, {__call = call, __index = index, __newindex = newindex})
if wprop.wibox == wb then
wprop.position = position
break
end
end
end
--- Reset all wiboxes positions.
local function update_all_wiboxes_position()
for _, wprop in ipairs(wiboxes) do
awfulwibox.set_position(wprop.wibox, wprop.position, wprop.screen)
end
end
local function call_wibox_position_hook_on_prop_update()
update_all_wiboxes_position()
end
local function wibox_update_strut(wb)
for _, wprop in ipairs(wiboxes) do
if wprop.wibox == wb then
if not wb.visible then
wb:struts { left = 0, right = 0, bottom = 0, top = 0 }
elseif wprop.position == "top" then
wb:struts { left = 0, right = 0, bottom = 0, top = wb.height + 2 * wb.border_width }
elseif wprop.position == "bottom" then
wb:struts { left = 0, right = 0, bottom = wb.height + 2 * wb.border_width, top = 0 }
elseif wprop.position == "left" then
wb:struts { left = wb.width + 2 * wb.border_width, right = 0, bottom = 0, top = 0 }
elseif wprop.position == "right" then
wb:struts { left = 0, right = wb.width + 2 * wb.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 wb The wibox to attach.
-- @param position The position of the wibox: top, bottom, left or right.
-- @param screen The screen to attach to
function awfulwibox.attach(wb, position, screen)
screen = get_screen(screen)
-- 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 == wb 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 = wb, position = position, screen = screen }, { __mode = 'v' }))
else
wibox_prop_table.position = position
end
wb:connect_signal("property::width", wibox_update_strut)
wb:connect_signal("property::height", wibox_update_strut)
wb:connect_signal("property::visible", wibox_update_strut)
wb:connect_signal("property::width", call_wibox_position_hook_on_prop_update)
wb:connect_signal("property::height", call_wibox_position_hook_on_prop_update)
wb:connect_signal("property::visible", call_wibox_position_hook_on_prop_update)
wb:connect_signal("property::border_width", call_wibox_position_hook_on_prop_update)
end
--- Align a wibox.
-- @param wb 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.
function awfulwibox.align(wb, align, screen)
screen = get_screen(screen)
local position = awfulwibox.get_position(wb)
local area = screen.workarea
if position == "right" then
if align == "right" then
wb.y = area.y
elseif align == "left" then
wb.y = area.y + area.height - (wb.height + 2 * wb.border_width)
elseif align == "center" then
wb.y = area.y + round((area.height - wb.height) / 2)
end
elseif position == "left" then
if align == "right" then
wb.y = (area.y + area.height) - (wb.height + 2 * wb.border_width)
elseif align == "left" then
wb.y = area.y
elseif align == "center" then
wb.y = area.y + round((area.height - wb.height) / 2)
end
elseif position == "bottom" then
if align == "right" then
wb.x = area.x + area.width - (wb.width + 2 * wb.border_width)
elseif align == "left" then
wb.x = area.x
elseif align == "center" then
wb.x = area.x + round((area.width - wb.width) / 2)
end
elseif position == "top" then
if align == "right" then
wb.x = area.x + area.width - (wb.width + 2 * wb.border_width)
elseif align == "left" then
wb.x = area.x
elseif align == "center" then
wb.x = area.x + round((area.width - wb.width) / 2)
end
end
-- Update struts regardless of changes
wibox_update_strut(wb)
end
--- Stretch a wibox so it takes all screen width or height.
-- @param wb The wibox.
-- @param screen The screen to stretch on, or the wibox screen.
function awfulwibox.stretch(wb, screen)
if screen then
screen = get_screen(screen)
local position = awfulwibox.get_position(wb)
local area = screen.workarea
if position == "right" or position == "left" then
wb.height = area.height - (2 * wb.border_width)
wb.y = area.y
else
wb.width = area.width - (2 * wb.border_width)
wb.x = area.x
end
end
end
--- Create a new wibox and attach it to a screen edge.
-- @see wibox
-- @param arg 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 awfulwibox.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.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 round(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 = round(screen.geometry.height * hp / 100)
end
end
end
else
arg.height = arg.height or round(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 = round(screen.geometry.width * wp / 100)
end
end
end
end
local w = wibox(arg)
w.visible = true
awfulwibox.attach(w, position, screen)
if has_to_stretch then
awfulwibox.stretch(w, screen)
else
awfulwibox.align(w, arg.align, screen)
end
awfulwibox.set_position(w, position, screen)
return w
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.connect_signal("property::struts", update_wiboxes_on_struts)
capi.client.connect_signal("unmanage", update_wiboxes_on_struts)
capi.screen.connect_signal("removed", function(s)
for _, wprop in ipairs(wiboxes) do
if wprop.screen == s then
wprop.wibox.visible = false
end
end
end)
function awfulwibox.mt:__call(...)
return awfulwibox.new(...)
end
return setmetatable(awfulwibox, awfulwibox.mt)
-- vim: filetype=lua:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:textwidth=80 -- vim: filetype=lua:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:textwidth=80

View File

@ -8,10 +8,9 @@
--------------------------------------------------------------------------- ---------------------------------------------------------------------------
local setmetatable = setmetatable local setmetatable = setmetatable
local capi = { screen = screen } local capi = { screen = screen, tag = tag }
local layout = require("awful.layout") local layout = require("awful.layout")
local tooltip = require("awful.tooltip") local tooltip = require("awful.tooltip")
local tag = require("awful.tag")
local beautiful = require("beautiful") local beautiful = require("beautiful")
local imagebox = require("wibox.widget.imagebox") local imagebox = require("wibox.widget.imagebox")
@ -48,8 +47,8 @@ function layoutbox.new(screen)
-- Do we already have the update callbacks registered? -- Do we already have the update callbacks registered?
if boxes == nil then if boxes == nil then
boxes = setmetatable({}, { __mode = "kv" }) boxes = setmetatable({}, { __mode = "kv" })
tag.attached_connect_signal(nil, "property::selected", update_from_tag) capi.tag.connect_signal("property::selected", update_from_tag)
tag.attached_connect_signal(nil, "property::layout", update_from_tag) capi.tag.connect_signal("property::layout", update_from_tag)
layoutbox.boxes = boxes layoutbox.boxes = boxes
end end
@ -60,7 +59,7 @@ function layoutbox.new(screen)
w._layoutbox_tooltip = tooltip({ objects = {w}, delay_show = 1 }) w._layoutbox_tooltip = tooltip({ objects = {w}, delay_show = 1 })
update(w, screen) update(w, screen)
boxes[screen] = w boxes[screen] = setmetatable({}, {__index = w, __newindex = w})
end end
return w return w

241
lib/gears/geometry.lua Normal file
View File

@ -0,0 +1,241 @@
---------------------------------------------------------------------------
--
-- Helper functions used to compute geometries.
--
-- When this module refer to a geometry table, this assume a table with at least
-- an *x*, *y*, *width* and *height* keys and numeric values.
--
-- @author Julien Danjou &lt;julien@danjou.info&gt;
-- @copyright 2008 Julien Danjou
-- @release @AWESOME_VERSION@
-- @module gears.geometry
---------------------------------------------------------------------------
local math = math
local gears = {geometry = {rectangle = {} } }
--- Get the square distance between a rectangle and a point.
-- @tparam table geom A rectangle
-- @tparam number geom.x The horizontal coordinate
-- @tparam number geom.y The vertical coordinate
-- @tparam number geom.width The rectangle width
-- @tparam number geom.height The rectangle height
-- @tparam number x X coordinate of point
-- @tparam number y Y coordinate of point
-- @treturn number The squared distance of the rectangle to the provided point
function gears.geometry.rectangle.get_square_distance(geom, x, y)
local dist_x, dist_y = 0, 0
if x < geom.x then
dist_x = geom.x - x
elseif x >= geom.x + geom.width then
dist_x = x - geom.x - geom.width + 1
end
if y < geom.y then
dist_y = geom.y - y
elseif y >= geom.y + geom.height then
dist_y = y - geom.y - geom.height + 1
end
return dist_x * dist_x + dist_y * dist_y
end
--- Return the closest rectangle from `list` for a given point.
-- @tparam table list A list of geometry tables.
-- @tparam number x The x coordinate
-- @tparam number y The y coordinate
-- @return The key from the closest geometry.
function gears.geometry.rectangle.get_closest_by_coord(list, x, y)
local dist = math.huge
local ret = nil
for k, v in pairs(list) do
local d = gears.geometry.rectangle.get_square_distance(v, x, y)
if d < dist then
ret, dist = k, d
end
end
return ret
end
--- Return the rectangle containing the [x, y] point.
--
-- Note that if multiple element from the geometry list contains the point, the
-- returned result is nondeterministic.
--
-- @tparam table list A list of geometry tables.
-- @tparam number x The x coordinate
-- @tparam number y The y coordinate
-- @return The key from the closest geometry. In case no result is found, *nil*
-- is returned.
function gears.geometry.rectangle.get_by_coord(list, x, y)
for k, geometry in pairs(list) do
if x >= geometry.x and x < geometry.x + geometry.width
and y >= geometry.y and y < geometry.y + geometry.height then
return k
end
end
end
--- Return true whether rectangle B is in the right direction
-- compared to rectangle A.
-- @param dir The direction.
-- @param gA The geometric specification for rectangle A.
-- @param gB The geometric specification for rectangle B.
-- @return True if B is in the direction of A.
local function is_in_direction(dir, gA, gB)
if dir == "up" then
return gA.y > gB.y
elseif dir == "down" then
return gA.y < gB.y
elseif dir == "left" then
return gA.x > gB.x
elseif dir == "right" then
return gA.x < gB.x
end
return false
end
--- Calculate distance between two points.
-- i.e: if we want to move to the right, we will take the right border
-- of the currently focused screen and the left side of the checked screen.
-- @param dir The direction.
-- @param _gA The first rectangle.
-- @param _gB The second rectangle.
-- @return The distance between the screens.
local function calculate_distance(dir, _gA, _gB)
local gAx = _gA.x
local gAy = _gA.y
local gBx = _gB.x
local gBy = _gB.y
if dir == "up" then
gBy = _gB.y + _gB.height
elseif dir == "down" then
gAy = _gA.y + _gA.height
elseif dir == "left" then
gBx = _gB.x + _gB.width
elseif dir == "right" then
gAx = _gA.x + _gA.width
end
return math.sqrt(math.pow(gBx - gAx, 2) + math.pow(gBy - gAy, 2))
end
--- Get the nearest rectangle in the given direction. Every rectangle is specified as a table
-- with *x*, *y*, *width*, *height* keys, the same as client or screen geometries.
-- @tparam string dir The direction, can be either *up*, *down*, *left* or *right*.
-- @tparam table recttbl A table of rectangle specifications.
-- @tparam table cur The current rectangle.
-- @return The index for the rectangle in recttbl closer to cur in the given direction. nil if none found.
function gears.geometry.rectangle.get_in_direction(dir, recttbl, cur)
local dist, dist_min
local target = nil
-- We check each object
for i, rect in pairs(recttbl) do
-- Check geometry to see if object is located in the right direction.
if is_in_direction(dir, cur, rect) then
-- Calculate distance between current and checked object.
dist = calculate_distance(dir, cur, rect)
-- If distance is shorter then keep the object.
if not target or dist < dist_min then
target = i
dist_min = dist
end
end
end
return target
end
--- Check if an area intersect another area.
-- @param a The area.
-- @param b The other area.
-- @return True if they intersect, false otherwise.
local function area_intersect_area(a, b)
return (b.x < a.x + a.width
and b.x + b.width > a.x
and b.y < a.y + a.height
and b.y + b.height > a.y)
end
--- Get the intersect area between a and b.
-- @tparam table a The area.
-- @tparam number a.x The horizontal coordinate
-- @tparam number a.y The vertical coordinate
-- @tparam number a.width The rectangle width
-- @tparam number a.height The rectangle height
-- @tparam table b The other area.
-- @tparam number b.x The horizontal coordinate
-- @tparam number b.y The vertical coordinate
-- @tparam number b.width The rectangle width
-- @tparam number b.height The rectangle height
-- @treturn table The intersect area.
function gears.geometry.rectangle.get_intersection(a, b)
local g = {}
g.x = math.max(a.x, b.x)
g.y = math.max(a.y, b.y)
g.width = math.min(a.x + a.width, b.x + b.width) - g.x
g.height = math.min(a.y + a.height, b.y + b.height) - g.y
return g
end
--- Remove an area from a list, splitting the space between several area that
-- can overlap.
-- @tparam table areas Table of areas.
-- @tparam table elem Area to remove.
-- @tparam number elem.x The horizontal coordinate
-- @tparam number elem.y The vertical coordinate
-- @tparam number elem.width The rectangle width
-- @tparam number elem.height The rectangle height
-- @return The new area list.
function gears.geometry.rectangle.area_remove(areas, elem)
for i = #areas, 1, -1 do
-- Check if the 'elem' intersect
if area_intersect_area(areas[i], elem) then
-- It does? remove it
local r = table.remove(areas, i)
local inter = gears.geometry.rectangle.get_intersection(r, elem)
if inter.x > r.x then
table.insert(areas, {
x = r.x,
y = r.y,
width = inter.x - r.x,
height = r.height
})
end
if inter.y > r.y then
table.insert(areas, {
x = r.x,
y = r.y,
width = r.width,
height = inter.y - r.y
})
end
if inter.x + inter.width < r.x + r.width then
table.insert(areas, {
x = inter.x + inter.width,
y = r.y,
width = (r.x + r.width) - (inter.x + inter.width),
height = r.height
})
end
if inter.y + inter.height < r.y + r.height then
table.insert(areas, {
x = r.x,
y = inter.y + inter.height,
width = r.width,
height = (r.y + r.height) - (inter.y + inter.height)
})
end
end
end
return areas
end
return gears.geometry

View File

@ -18,7 +18,7 @@ return
matrix = require("gears.matrix"); matrix = require("gears.matrix");
shape = require("gears.shape"); shape = require("gears.shape");
protected_call = require("gears.protected_call"); protected_call = require("gears.protected_call");
screen = require("gears.screen"); geometry = require("gears.geometry");
} }
-- vim: filetype=lua:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:textwidth=80 -- vim: filetype=lua:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:textwidth=80

View File

@ -1,29 +0,0 @@
---------------------------------------------------------------------------
-- @author Uli Schlachter
-- @copyright 2016 Uli Schlachter
-- @release @AWESOME_VERSION@
-- @classmod gears.screen
---------------------------------------------------------------------------
local ascreen = require("awful.screen")
local util = require("awful.util")
local module = {}
--- Call a function for each existing and created-in-the-future screen.
-- @tparam function func The function to call.
function module.connect_for_each_screen(func)
util.deprecate("Use awful.screen.connect_for_each_screen")
ascreen.connect_for_each_screen(func)
end
--- Undo the effect of connect_for_each_screen.
-- @tparam function func The function that should no longer be called.
function module.disconnect_for_each_screen(func)
util.deprecate("Use awful.screen.disconnect_for_each_screen")
ascreen.disconnect_for_each_screen(func)
end
return module
-- vim: filetype=lua:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:textwidth=80

View File

@ -19,30 +19,26 @@ local color = require("gears.color")
local object = require("gears.object") local object = require("gears.object")
local surface = require("gears.surface") local surface = require("gears.surface")
local timer = require("gears.timer") local timer = require("gears.timer")
local grect = require("gears.geometry").rectangle
local matrix = require("gears.matrix") local matrix = require("gears.matrix")
local hierarchy = require("wibox.hierarchy") local hierarchy = require("wibox.hierarchy")
local unpack = unpack or table.unpack -- luacheck: globals unpack (compatibility with Lua 5.1) local unpack = unpack or table.unpack -- luacheck: globals unpack (compatibility with Lua 5.1)
local drawables = setmetatable({}, { __mode = 'k' }) local drawables = setmetatable({}, { __mode = 'k' })
-- This is awful.screen.getbycoord() which we sadly cannot use from here (cyclic
-- dependencies are bad!)
local function screen_getbycoord(x, y)
for i in screen do
local geometry = screen[i].geometry
if x >= geometry.x and x < geometry.x + geometry.width
and y >= geometry.y and y < geometry.y + geometry.height then
return capi.screen[i]
end
end
return capi.screen.primary
end
-- Get the widget context. This should always return the same table (if -- Get the widget context. This should always return the same table (if
-- possible), so that our draw and fit caches can work efficiently. -- possible), so that our draw and fit caches can work efficiently.
local function get_widget_context(self) local function get_widget_context(self)
local geom = self.drawable:geometry() local geom = self.drawable:geometry()
local s = screen_getbycoord(geom.x, geom.y)
local sgeos = {}
for s in capi.screen do
sgeos[s] = s.geometry
end
local s = grect.get_by_coord(sgeos, geom.x, geom.y) or capi.screen.primary
local context = self._widget_context local context = self._widget_context
local dpi = beautiful.xresources.get_dpi(s) local dpi = beautiful.xresources.get_dpi(s)
if (not context) or context.screen ~= s or context.dpi ~= dpi then if (not context) or context.screen ~= s or context.dpi ~= dpi then

View File

@ -8,12 +8,14 @@
local capi = { local capi = {
drawin = drawin, drawin = drawin,
root = root, root = root,
awesome = awesome awesome = awesome,
screen = screen
} }
local setmetatable = setmetatable local setmetatable = setmetatable
local pairs = pairs local pairs = pairs
local type = type local type = type
local object = require("gears.object") local object = require("gears.object")
local grect = require("gears.geometry").rectangle
local beautiful = require("beautiful") local beautiful = require("beautiful")
local base = require("wibox.widget.base") local base = require("wibox.widget.base")
@ -26,234 +28,57 @@ wibox.widget = require("wibox.widget")
wibox.drawable = require("wibox.drawable") wibox.drawable = require("wibox.drawable")
wibox.hierarchy = require("wibox.hierarchy") wibox.hierarchy = require("wibox.hierarchy")
--- Set the widget that the wibox displays local force_forward = {
shape_bounding = true,
shape_clip = true,
}
--@DOC_wibox_COMMON@
function wibox:set_widget(widget) function wibox:set_widget(widget)
self._drawable:set_widget(widget) self._drawable:set_widget(widget)
end end
-- Import some drawin documentation function wibox:get_widget()
return self._drawable.widget
end
--- Border width.
--
-- **Signal:**
--
-- * *property::border_width*
--
-- @property border_width
-- @param integer
--- Border color.
--
-- Please note that this property only support string based 24 bit or 32 bit
-- colors:
--
-- Red Blue
-- _| _|
-- #FF00FF
-- T‾
-- Green
--
--
-- Red Blue
-- _| _|
-- #FF00FF00
-- T‾ ‾T
-- Green Alpha
--
-- **Signal:**
--
-- * *property::border_color*
--
-- @property border_color
-- @param string
--- On top of other windows.
--
-- **Signal:**
--
-- * *property::ontop*
--
-- @property ontop
-- @param boolean
--- The mouse cursor.
--
-- **Signal:**
--
-- * *property::cursor*
--
-- @property cursor
-- @param string
-- @see mouse
--- Visibility.
--
-- **Signal:**
--
-- * *property::visible*
--
-- @property visible
-- @param boolean
--- The opacity of the wibox, between 0 and 1.
--
-- **Signal:**
--
-- * *property::opacity*
--
-- @property opacity
-- @tparam number opacity (between 0 and 1)
--- The window type (desktop, normal, dock, ...).
--
-- **Signal:**
--
-- * *property::type*
--
-- @property type
-- @param string
-- @see client.type
--- The x coordinates.
--
-- **Signal:**
--
-- * *property::x*
--
-- @property x
-- @param integer
--- The y coordinates.
--
-- **Signal:**
--
-- * *property::y*
--
-- @property y
-- @param integer
--- The width of the wibox.
--
-- **Signal:**
--
-- * *property::width*
--
-- @property width
-- @param width
--- The height of the wibox.
--
-- **Signal:**
--
-- * *property::height*
--
-- @property height
-- @param height
--- The wibox's `drawable`.
--
-- **Signal:**
--
-- * *property::drawable*
--
-- @property drawable
-- @tparam drawable drawable
--- The X window id.
--
-- **Signal:**
--
-- * *property::window*
--
-- @property window
-- @param string
-- @see client.window
--- The wibox's bounding shape as a (native) cairo surface.
--
-- **Signal:**
--
-- * *property::shape_bounding*
--
-- @property shape_bounding
-- @param surface._native
--- The wibox's clip shape as a (native) cairo surface.
--
-- **Signal:**
--
-- * *property::shape_clip*
--
-- @property shape_clip
-- @param surface._native
--- Get or set mouse buttons bindings to a wibox.
--
-- @param buttons_table A table of buttons objects, or nothing.
-- @function wibox.buttons
--- Get or set wibox geometry. That's the same as accessing or setting the x,
-- y, width or height properties of a wibox.
--
-- @param A table with coordinates to modify.
-- @return A table with wibox coordinates and geometry.
-- @function wibox.geometry
--- Get or set wibox struts.
--
-- @param strut A table with new strut, or nothing
-- @return The wibox strut in a table.
-- @function wibox.struts
-- @see client.struts
--- The default background color.
-- @beautiful beautiful.bg_normal
-- @see set_bg
--- The default foreground (text) color.
-- @beautiful beautiful.fg_normal
-- @see set_fg
--- Set a declarative widget hierarchy description.
-- See [The declarative layout system](../documentation/03-declarative-layout.md.html)
-- @param args An array containing the widgets disposition
-- @name setup
-- @class function
wibox.setup = base.widget.setup wibox.setup = base.widget.setup
--- Set the background of the wibox
-- @param c The background to use. This must either be a cairo pattern object,
-- nil or a string that gears.color() understands.
function wibox:set_bg(c) function wibox:set_bg(c)
self._drawable:set_bg(c) self._drawable:set_bg(c)
end end
--- Set the background image of the drawable
-- If `image` is a function, it will be called with `(context, cr, width, height)`
-- as arguments. Any other arguments passed to this method will be appended.
-- @param image A background image or a function
function wibox:set_bgimage(image, ...) function wibox:set_bgimage(image, ...)
self._drawable:set_bgimage(image, ...) self._drawable:set_bgimage(image, ...)
end end
--- Set the foreground of the wibox
-- @param c The foreground to use. This must either be a cairo pattern object,
-- nil or a string that gears.color() understands.
function wibox:set_fg(c) function wibox:set_fg(c)
self._drawable:set_fg(c) self._drawable:set_fg(c)
end end
--- Find a widget by a point.
-- The wibox must have drawn itself at least once for this to work.
-- @tparam number x X coordinate of the point
-- @tparam number y Y coordinate of the point
-- @treturn table A sorted table of widgets positions. The first element is the biggest
-- container while the last is the topmost widget. The table contains *x*, *y*,
-- *width*, *height* and *widget*.
function wibox:find_widgets(x, y) function wibox:find_widgets(x, y)
return self._drawable:find_widgets(x, y) return self._drawable:find_widgets(x, y)
end end
function wibox:get_screen()
local sgeos = {}
for s in capi.screen do
sgeos[s] = s.geometry
end
return grect.get_closest_by_coord(sgeos, self.x, self.y)
end
function wibox:set_screen(s)
s = capi.screen[s or 1]
if s ~= self:get_screen() then
self.x = s.geometry.x
self.y = s.geometry.y
end
end
for _, k in pairs{ "buttons", "struts", "geometry", "get_xproperty", "set_xproperty" } do for _, k in pairs{ "buttons", "struts", "geometry", "get_xproperty", "set_xproperty" } do
wibox[k] = function(self, ...) wibox[k] = function(self, ...)
return self.drawin[k](self.drawin, ...) return self.drawin[k](self.drawin, ...)
@ -297,6 +122,7 @@ local function setup_signals(_wibox)
end end
local function new(args) local function new(args)
args = args or {}
local ret = object() local ret = object()
local w = capi.drawin(args) local w = capi.drawin(args)
@ -339,10 +165,24 @@ local function new(args)
-- Make sure the wibox is drawn at least once -- Make sure the wibox is drawn at least once
ret.draw() ret.draw()
-- Redirect all non-existing indexes to the "real" drawin -- If a value is not found, look in the drawin
setmetatable(ret, { setmetatable(ret, {
__index = w, __index = function(self, k)
__newindex = w if rawget(self, "get_"..k) then
return self["get_"..k](self)
else
return w[k]
end
end,
__newindex = function(self, k,v)
if rawget(self, "set_"..k) then
self["set_"..k](self, v)
elseif w[k] ~= nil or force_forward[k] then
w[k] = v
else
rawset(self, k, v)
end
end
}) })
return ret return ret

View File

@ -3423,21 +3423,12 @@ client_class_setup(lua_State *L)
signal_add(&client_class.signals, "property::transient_for"); signal_add(&client_class.signals, "property::transient_for");
signal_add(&client_class.signals, "property::type"); signal_add(&client_class.signals, "property::type");
signal_add(&client_class.signals, "property::urgent"); signal_add(&client_class.signals, "property::urgent");
/**
* @signal property::width
*/
signal_add(&client_class.signals, "property::width"); signal_add(&client_class.signals, "property::width");
/** /**
* @signal property::window * @signal property::window
*/ */
signal_add(&client_class.signals, "property::window"); signal_add(&client_class.signals, "property::window");
/**
* @signal property::x
*/
signal_add(&client_class.signals, "property::x"); signal_add(&client_class.signals, "property::x");
/**
* @signal property::y
*/
signal_add(&client_class.signals, "property::y"); signal_add(&client_class.signals, "property::y");
/** When a client should get activated (focused and/or raised). /** When a client should get activated (focused and/or raised).
* *

View File

@ -30,7 +30,7 @@
* *
* It is also possible loop over all current screens using: * It is also possible loop over all current screens using:
* *
* for s, screen do * for s in screen do
* -- do something * -- do something
* end * end
* *
@ -117,7 +117,7 @@
* The screen workarea. * The screen workarea.
* *
* The workarea is a subsection of the screen where clients can be placed. It * The workarea is a subsection of the screen where clients can be placed. It
* usually excludes the toolbars (see `awful.wibox`) and dockable clients * usually excludes the toolbars (see `awful.wibar`) and dockable clients
* (see `client.dockable`) like WindowMaker DockAPP. * (see `client.dockable`) like WindowMaker DockAPP.
* *
* It can be modified be altering the `wibox` or `client` struts. * It can be modified be altering the `wibox` or `client` struts.
@ -756,8 +756,8 @@ void screen_update_workarea(screen_t *screen)
area.x += left; area.x += left;
area.y += top; area.y += top;
area.width -= left + right; area.width -= MIN(area.width, left + right);
area.height -= top + bottom; area.height -= MIN(area.height, top + bottom);
if (AREA_EQUAL(area, screen->workarea)) if (AREA_EQUAL(area, screen->workarea))
return; return;

View File

@ -6,3 +6,6 @@ mouse.coords {x= 50, y=50} --DOC_HIDE
mouse.push_history() --DOC_HIDE mouse.push_history() --DOC_HIDE
awful.placement.under_mouse(client.focus) awful.placement.under_mouse(client.focus)
assert(client.focus.x + client.focus.width /2 - mouse.coords().x <= 1) --DOC_HIDE
assert(client.focus.y + client.focus.height/2 - mouse.coords().y <= 1) --DOC_HIDE

View File

@ -1,11 +1,12 @@
local file_path, image_path, luacovpath = ... local file_path, image_path, luacovpath = ...
-- Set the global shims -- Set the global shims
-- luacheck: globals awesome client tag drawin -- luacheck: globals awesome client tag drawin screen
awesome = require( "awesome" ) awesome = require( "awesome" )
client = require( "client" ) client = require( "client" )
tag = require( "tag" ) tag = require( "tag" )
drawin = require( "drawin" ) drawin = require( "drawin" )
screen = require( "screen" )
-- Force luacheck to be silent about setting those as unused globals -- Force luacheck to be silent about setting those as unused globals
assert(awesome and client and tag) assert(awesome and client and tag)

View File

@ -4,6 +4,7 @@ local mouse = mouse
local class = tag local class = tag
local obj = class({}) local obj = class({})
local handler = require("gears.object.properties") local handler = require("gears.object.properties")
local wibox = require("wibox")
awesome.connect_signal("debug::index::miss", error) awesome.connect_signal("debug::index::miss", error)
awesome.connect_signal("debug::newindex::miss", error) awesome.connect_signal("debug::newindex::miss", error)
@ -35,4 +36,8 @@ assert(obj.key == 1337)
mouse.foo = "bar" mouse.foo = "bar"
assert(mouse.foo == "bar") assert(mouse.foo == "bar")
local w = wibox()
w.foo = "bar"
assert(w.foo == "bar")
require("_runner").run_steps({ function() return true end }) require("_runner").run_steps({ function() return true end })

322
tests/test-struts.lua Normal file
View File

@ -0,0 +1,322 @@
local placement = require("awful.placement")
local wibox = require("wibox")
local wibar = require("awful.wibar")
-- luacheck: globals mywibox
local steps = {}
local parent, small
local twibar, bwibar, lwibar, rwibar = mywibox[screen.primary]
-- Test the struts without using wibars
table.insert(steps, function()
local sgeo = screen.primary.geometry
-- Manually place at the bottom right
local w = wibox{
height = 50,
width = 50,
y = sgeo.y + sgeo.height - 50,
x = sgeo.x + sgeo.width - 50,
visible = true,
}
w:struts {
left = 0,
top = 0,
bottom = 50,
right = 50,
}
local wa = screen.primary.workarea
assert(wa.x == 0 )
assert(wa.y == twibar.height )
assert(wa.width == sgeo.width - 50 )
assert(wa.height == sgeo.height - 50 - twibar.height)
w:struts {
left = 0,
top = 0,
bottom = 0,
right = 0,
}
w.visible = false
wa = screen.primary.workarea
assert(wa.x == 0 )
assert(wa.y == twibar.height )
assert(wa.width == sgeo.width )
assert(wa.height == sgeo.height - twibar.height)
return true
end)
-- Test "attach" for wibox/client to wibox
table.insert(steps, function()
parent = wibox {
visible = true,
width = 500,
height = 500,
}
placement.centered(parent)
small = wibox {
bg = "#ff0000",
height = 24,
ontop = true,
visible = true,
}
-- Add an attached function
placement.top_left(small, {parent = parent})
placement.maximize_horizontally(small, {parent = parent, attach = true})
return true
end)
table.insert(steps, function()
assert(parent:geometry().width == 500)
assert(parent:geometry().height == 500)
assert(parent:geometry().y == small:geometry().y)
assert(parent:geometry().x == small:geometry().x)
assert(parent:geometry().width == small:geometry().width )
assert(small:geometry().height == 24)
-- Now, move the parent and see of the attached one is correctly updated
placement.stretch_left(parent)
return true
end)
table.insert(steps, function()
assert(parent:geometry().y == small:geometry().y)
assert(parent:geometry().x == small:geometry().x)
assert(parent:geometry().width == small:geometry().width )
assert(small:geometry().height == 24)
-- Do the same, but with placement compositing
small = wibox {
bg = "#ff0000",
height = 50,
width = 50,
ontop = true,
visible = true,
}
local p = placement.bottom_right + placement.scale
p(small, {parent=parent, attach=true, direction="left", to_percent=0.5})
return true
end)
local function check_ratio()
assert(
(
parent:geometry().y
+ parent:geometry().height
- small :geometry().height
) == small:geometry().y
)
assert(math.abs(parent:geometry().x + math.ceil(parent:geometry().width/2) - small:geometry().x) <= 1)
assert(math.abs(math.ceil(parent:geometry().width/2) - small:geometry().width) <= 1)
assert(small:geometry().height == 50)
end
table.insert(steps, function()
check_ratio()
-- Now, do some more transformation on the parent and make sure the "small"
-- wibox follow.
placement.scale (parent, {direction="left", to_percent=0.2})
placement.scale (parent, {direction="up" , to_percent=0.2})
placement.bottom (parent )
placement.top_right(parent )
placement.centered (parent )
return true
end)
-- Do the same checks to see if everything has followed as expected
table.insert(steps, function()
check_ratio()
-- Now, test if the wibar has updated the workarea.
placement.maximize(parent, {honor_workarea = true})
return true
end)
table.insert(steps, function()
local wa = screen.primary.workarea
local sgeo = screen.primary.geometry
local wgeo = twibar:geometry()
assert(wa.width == sgeo.width )
assert(wa.x == sgeo.x )
assert(wa.y == sgeo.y + wgeo.height )
-- assert(wa.height == sgeo.height - wgeo.height)
-- assert(wgeo.height == sgeo.height - wa.height )
assert(parent.y == wa.y )
assert(parent.x == wa.x )
assert(parent.width == wa.width )
assert(parent.height == wa.height )
-- Add more wibars
bwibar = wibar {position = "bottom", bg = "#00ff00"}
lwibar = wibar {position = "left" , bg = "#0000ff"}
rwibar = wibar {position = "right" , bg = "#ff00ff"}
return true
end)
-- Make sure the maximized client has the right size and position
local function check_maximize()
local pgeo = parent:geometry()
local margins = {left=0, right=0, top=0, bottom=0}
for _, w in ipairs {twibar, lwibar, rwibar, bwibar} do
if w.visible then
local pos = w.position
local w_or_h = (pos == "left" or pos == "right") and "width" or "height"
margins[pos] = margins[pos] + w[w_or_h]
end
end
local sgeo = parent.screen.geometry
sgeo.x = sgeo.x + margins.left
sgeo.y = sgeo.y + margins.top
sgeo.width = sgeo.width - margins.left - margins.right
sgeo.height = sgeo.height - margins.top - margins.bottom
local wa = parent.screen.workarea
for k, v in pairs(wa) do
assert(sgeo[k] == v)
end
assert(sgeo.width == pgeo.width + 2*parent.border_width)
assert(sgeo.height == pgeo.height + 2*parent.border_width)
assert(sgeo.x == pgeo.x )
assert(sgeo.y == pgeo.y )
end
table.insert(steps, function()
-- Attach the parent wibox to it is updated along the workarea
placement.maximize(parent, {honor_workarea = true, attach = true})
-- Make the wibox more visible
parent.border_color = "#ffff00"
parent.border_width = 10
parent.ontop = true
-- parent.visible = false
local wa = screen.primary.workarea
local sgeo = screen.primary.geometry
assert(lwibar.width == rwibar.width )
assert(lwibar.height == rwibar.height )
assert(twibar.x == bwibar.x )
assert(twibar.width == bwibar.width )
-- Check the left wibar size and position
assert(lwibar.x == wa.x - lwibar.width )
assert(lwibar.height == wa.height )
-- Check the right wibar size and position
assert(rwibar.x == wa.x + wa.width )
assert(rwibar.height == wa.height )
-- Check the bottom wibar size and position
assert(bwibar.width == sgeo.width )
assert(bwibar.x == 0 )
return true
end)
table.insert(steps, function()
check_maximize()
-- There should be a detach callback
assert(lwibar.detach_callback)
-- Begin to move wibars around
lwibar.position = "top"
assert(lwibar.position == "top")
assert(lwibar.y == twibar.height)
return true
end)
table.insert(steps, function()
check_maximize()
bwibar.position = "right"
bwibar.ontop = true
rwibar.ontop = true
assert(bwibar.position == "right")
return true
end)
table.insert(steps, function()
check_maximize()
rwibar.position = "top"
return true
end)
table.insert(steps, function()
check_maximize()
bwibar.position = "top"
return true
end)
table.insert(steps, function()
check_maximize()
for _, w in ipairs {twibar, lwibar, rwibar, bwibar} do
w.position = "right"
end
return true
end)
-- Test visibility
table.insert(steps, function()
check_maximize()
twibar.visible = false
rwibar.visible = false
return true
end)
table.insert(steps, function()
check_maximize()
twibar.visible = true
return true
end)
table.insert(steps, function()
check_maximize()
return true
end)
require("_runner").run_steps(steps)