awful.placement: Add `align` method

This allow to place a client, wibox or cursor at the
edges, corners or center of the parent geometry.

This also add code from `awful.wibox` to ajust the workarea.
Future commit will use `awful.placement` to place `awful.wibox`.
This commit is contained in:
Emmanuel Lepage Vallee 2016-03-18 02:22:45 -04:00
parent 61d143fe98
commit 456f0bb70c
1 changed files with 149 additions and 16 deletions

View File

@ -38,6 +38,25 @@ local corners3x3 = {{"top_left" , "top" , "top_right" },
local corners2x2 = {{"top_left" , "top_right" }, local corners2x2 = {{"top_left" , "top_right" },
{"bottom_left", "bottom_right"}} {"bottom_left", "bottom_right"}}
-- Compute the new `x` and `y`.
-- The workarea position need to be applied by the caller
local align_map = {
top_left = function(_ , _ , _ , _ ) return {x=0 , y=0 } end,
top_right = function(sw, _ , dw, _ ) return {x=sw-dw , y=0 } end,
bottom_left = function(_ , sh, _ , dh) return {x=0 , y=sh-dh } end,
bottom_right = function(sw, sh, dw, dh) return {x=sw-dw , y=sh-dh } end,
left = function(_ , sh, _ , dh) return {x=0 , y=sh/2-dh/2} end,
right = function(sw, sh, dw, dh) return {x=sw-dw , y=sh/2-dh/2} end,
top = function(sw, _ , dw, _ ) return {x=sw/2-dw/2, y=0 } end,
bottom = function(sw, sh, dw, dh) return {x=sw/2-dw/2, y=sh-dh } end,
centered = function(sw, sh, dw, dh) return {x=sw/2-dw/2, y=sh/2-dh/2} end,
center_vertical = function(_ , sh, _ , dh) return {x= nil , y=sh-dh } end,
center_horizontal = function(sw, _ , dw, _ ) return {x=sw/2-dw/2, y= nil } end,
}
-- Store function -> keys
local reverse_align_map = {}
--- 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
@ -116,21 +135,6 @@ local function get_parent_geometry(obj, args)
end end
end end
--- Convert a rectangle and matrix coordinates info into a point.
-- This is useful along with matrixes like `corners3x3` to convert
-- indices into a geometry point.
-- @tparam table geo a geometry table
-- @tparam number corner_i The horizontal matrix index
-- @tparam number corner_j The vertical matrix index
-- @tparam number n The (square) matrix dimension
-- @treturn table A table with *x* and *y* keys
local function rect_to_point(geo, corner_i, corner_j, n)
return {
x = geo.x + corner_i * math.floor(geo.width / (n-1)),
y = geo.y + corner_j * math.floor(geo.height / (n-1)),
}
end
--- Move a point into an area. --- Move a point into an area.
-- This doesn't change the *width* and *height* values, allowing the target -- This doesn't change the *width* and *height* values, allowing the target
-- area to be smaller than the source one. -- area to be smaller than the source one.
@ -157,6 +161,89 @@ local function move_into_geometry(source, target)
return ret return ret
end end
-- Update the workarea
local function wibox_update_strut(d, position)
-- If the drawable isn't visible, remove the struts
if not d.visible then
d:struts { left = 0, right = 0, bottom = 0, top = 0 }
return
end
-- Detect horizontal or vertical drawables
local geo = area_common(d)
local vertical = geo.width < geo.height
-- Look into the `position` string to find the relevants sides to crop from
-- the workarea
local struts = { left = 0, right = 0, bottom = 0, top = 0 }
if vertical then
for _, v in ipairs {"right", "left"} do
if (not position) or position:match(v) then
struts[v] = geo.width
end
end
else
for _, v in ipairs {"top", "bottom"} do
if (not position) or position:match(v) then
struts[v] = geo.height
end
end
end
-- Update the workarea
d:struts(struts)
end
--- Pin a drawable to a placement function.
-- Automatically update the position when the size change.
-- All other arguments will be passed to the `position` function (if any)
-- @tparam[opt=client.focus] drawable d A drawable (like `client`, `mouse`
-- or `wibox`)
-- @param position_f A position name (see `align`) or a position function
-- @tparam[opt={}] table args Other arguments
local function attach(d, position_f, args)
args = args or {}
if not args.attach then return end
d = d or capi.client.focus
if not d then return end
if type(position_f) == "string" then
position_f = placement[position_f]
end
if not position_f then return end
local function tracker()
position_f(d, args)
end
d:connect_signal("property::width" , tracker)
d:connect_signal("property::height", tracker)
tracker()
if args.update_workarea then
local function tracker_struts()
--TODO this is too fragile and doesn't work with all methods.
wibox_update_strut(d, reverse_align_map[position_f])
end
d:connect_signal("property::geometry" , tracker_struts)
d:connect_signal("property::visible" , tracker_struts)
tracker_struts()
end
-- If there is a parent drawable, screen or mouse, also track it
local parent = args.parent or d.screen
if parent then
args.parent:connect_signal("property::geometry" , tracker)
end
end
--- Check if an area intersect another area. --- Check if an area intersect another area.
-- @param a The area. -- @param a The area.
-- @param b The other area. -- @param b The other area.
@ -276,7 +363,7 @@ function placement.closest_corner(d, args)
-- Transpose the corner back to the original size -- Transpose the corner back to the original size
local new_args = setmetatable({position = corner}, {__index=args}) local new_args = setmetatable({position = corner}, {__index=args})
geometry_common(d, new_args, rect_to_point(dgeo, corner_i, corner_j , n)) placement.align(d, new_args)
return corner return corner
end end
@ -409,6 +496,52 @@ function placement.next_to_mouse(c, offset)
return c:geometry({ x = x, y = y }) return c:geometry({ x = x, y = y })
end end
--- Move the drawable (client or wibox) `d` to a screen position or side.
--
-- Supported args.positions are:
--
-- * top_left
-- * top_right
-- * bottom_left
-- * bottom_right
-- * left
-- * right
-- * top
-- * bottom
-- * centered
-- * center_vertical
-- * center_horizontal
--
--@DOC_awful_placement_align_EXAMPLE@
-- @tparam drawable d A drawable (like `client`, `mouse` or `wibox`)
-- @tparam[opt={}] table args Other arguments
function placement.align(d, args)
args = args or {}
d = d or capi.client.focus
if not d or not args.position then return end
local sgeo = get_parent_geometry(d, args)
local dgeo = geometry_common(d, args)
local bw = d.border_width or 0
local pos = align_map[args.position](
sgeo.width ,
sgeo.height,
dgeo.width ,
dgeo.height
)
geometry_common(d, args, {
x = (pos.x and math.ceil(sgeo.x + pos.x) or dgeo.x) + bw ,
y = (pos.y and math.ceil(sgeo.y + pos.y) or dgeo.y) + bw ,
width = math.ceil(dgeo.width ) - 2*bw,
height = math.ceil(dgeo.height ) - 2*bw,
})
attach(d, placement[args.position], args)
end
--- Place the client centered with respect to a parent or the clients screen. --- Place the client centered with respect to a parent or the clients screen.
-- @param c The client. -- @param c The client.
-- @param[opt] p The parent (nil for screen centering). -- @param[opt] p The parent (nil for screen centering).