From ea40fbd198d22f331ba1d28b8fb910dd251d876d Mon Sep 17 00:00:00 2001 From: Emmanuel Lepage Vallee Date: Thu, 31 Mar 2016 05:04:10 -0400 Subject: [PATCH 01/17] shims: Add property::workarea --- tests/examples/shims/screen.lua | 2 ++ 1 file changed, 2 insertions(+) diff --git a/tests/examples/shims/screen.lua b/tests/examples/shims/screen.lua index d7f25860d..6fe43a823 100644 --- a/tests/examples/shims/screen.lua +++ b/tests/examples/shims/screen.lua @@ -95,6 +95,8 @@ end screen._add_screen {width=320, height=240} +screen.add_signal("property::workarea") + return screen -- vim: filetype=lua:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:textwidth=80 From 5a5f0167cfac73a2af2ab4da44cdb45d2f6bf10c Mon Sep 17 00:00:00 2001 From: Emmanuel Lepage Vallee Date: Fri, 18 Mar 2016 01:25:36 -0400 Subject: [PATCH 02/17] awful.placement: Add `closest_corner` This commit also add many local helper functions. They will be re-used by the next few commits. --- lib/awful/placement.lua | 203 ++++++++++++++++++++++++++++++++++++---- 1 file changed, 183 insertions(+), 20 deletions(-) diff --git a/lib/awful/placement.lua b/lib/awful/placement.lua index 7f07ce1c9..f75c9d225 100644 --- a/lib/awful/placement.lua +++ b/lib/awful/placement.lua @@ -29,6 +29,134 @@ end local placement = {} +-- 3x3 matrix of the valid sides and corners +local corners3x3 = {{"top_left" , "top" , "top_right" }, + {"left" , nil , "right" }, + {"bottom_left", "bottom" , "bottom_right"}} + +-- 2x2 matrix of the valid sides and corners +local corners2x2 = {{"top_left" , "top_right" }, + {"bottom_left", "bottom_right"}} + +--- Get the area covered by a drawin. +-- @param d The drawin +-- @tparam[opt=nil] table new_geo A new geometry +-- @tparam[opt=false] boolean ignore_border_width Ignore the border +-- @treturn The drawin's area. +local function area_common(d, new_geo, ignore_border_width) + -- The C side expect no arguments, nil isn't valid + 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 + geometry.x = geometry.x - border + geometry.y = geometry.y - border + geometry.width = geometry.width + 2 * border + geometry.height = geometry.height + 2 * border + return geometry +end + +--- Get (and optionally set) an object geometry. +-- Some elements, such as `mouse` and `screen` don't have a `:geometry()` +-- methods. +-- @param obj An object +-- @tparam table args the method arguments +-- @tparam[opt=nil] table new_geo A new geometry to replace the existing one +-- @tparam[opt=false] boolean ignore_border_width Ignore the border +-- @treturn table A table with *x*, *y*, *width* and *height*. +local function geometry_common(obj, args, new_geo, ignore_border_width) + -- It's a mouse + if obj.coords then + local coords = new_geo and obj.coords(new_geo) or obj.coords() + return {x=coords.x, y=coords.y, width=0, height=0} + elseif obj.geometry then + local geo = obj.geometry + + -- It is either a drawable or something that implement its API + if type(geo) == "function" then + local dgeo = area_common(obj, new_geo, ignore_border_width) + + -- Apply the margins + if args.margins then + local delta = type(args.margins) == "table" and args.margins or { + left = args.margins , right = args.margins, + top = args.margins , bottom = args.margins + } + + return { + x = dgeo.x + (delta.left or 0), + y = dgeo.y + (delta.top or 0), + width = dgeo.width - (delta.left or 0) - (delta.right or 0), + height = dgeo.height - (delta.top or 0) - (delta.bottom or 0), + } + end + + return dgeo + end + + -- It is a screen, it doesn't support setting new sizes. + return a_screen.get_bounding_geometry(obj, args) + else + assert(false, "Invalid object") + end +end + +--- Get the parent geometry from the standardized arguments API shared by all +-- `awful.placement` methods. +-- @param obj A screen or a drawable +-- @tparam table args the method arguments +-- @treturn table A table with *x*, *y*, *width* and *height*. +local function get_parent_geometry(obj, args) + if args.bounding_rect then + return args.bounding_rect + elseif args.parent then + return geometry_common(args.parent, args) + elseif obj.screen then + return geometry_common(obj.screen, args) + else + return geometry_common(capi.screen[capi.mouse.screen], args) + 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. +-- This doesn't change the *width* and *height* values, allowing the target +-- area to be smaller than the source one. +-- @tparam table source The (larger) geometry to move `target` into +-- @tparam table target The area to move into `source` +-- @treturn table A table with *x* and *y* keys +local function move_into_geometry(source, target) + local ret = {x = target.x, y = target.y} + + -- Horizontally + if ret.x < source.x then + ret.x = source.x + elseif ret.x > source.x + source.width then + ret.x = source.x + source.width - 1 + end + + -- Vertically + if ret.y < source.y then + ret.y = source.y + elseif ret.y > source.y + source.height then + ret.y = source.y + source.height - 1 + end + + return ret +end + --- Check if an area intersect another area. -- @param a The area. -- @param b The other area. @@ -107,15 +235,50 @@ local function area_remove(areas, elem) return areas end ---- Get the area covered by a client. --- @client c The client. --- @treturn The client's area. -local function get_area(c) - local geometry = c:geometry() - local border = c.border_width or 0 - geometry.width = geometry.width + 2 * border - geometry.height = geometry.height + 2 * border - return geometry +--- Move a drawable to the closest corner of the parent geometry (such as the +-- screen). +-- +-- Valid arguments include the common ones and: +-- +-- * **include_sides**: Also include the left, right, top and bottom positions +-- +--@DOC_awful_placement_closest_mouse_EXAMPLE@ +-- @tparam[opt=client.focus] drawable d A drawable (like `client`, `mouse` +-- or `wibox`) +-- @tparam[opt={}] table args The arguments +-- @treturn string The corner name +function placement.closest_corner(d, args) + d = d or capi.client.focus + + local sgeo = get_parent_geometry(d, args) + local dgeo = geometry_common(d, args) + + local pos = move_into_geometry(sgeo, dgeo) + + local corner_i, corner_j, n + + -- Use the product of 3 to get the closest point in a NxN matrix + local function f(_n, mat) + n = _n + corner_i = -math.ceil( ( (sgeo.x - pos.x) * n) / sgeo.width ) + corner_j = -math.ceil( ( (sgeo.y - pos.y) * n) / sgeo.height ) + return mat[corner_j + 1][corner_i + 1] + end + + -- Turn the area into a grid and snap to the cloest point. This size of the + -- grid will increase the accuracy. A 2x2 matrix only include the corners, + -- at 3x3, this include the sides too technically, a random size would work, + -- but without corner names. + local grid_size = args.include_sides and 3 or 2 + + -- If the point is in the center, use the closest corner + local corner = f(grid_size, corners3x3) or f(2, corners2x2) + + -- Transpose the corner back to the original size + local new_args = setmetatable({position = corner}, {__index=args}) + geometry_common(d, new_args, rect_to_point(dgeo, corner_i, corner_j , n)) + + return corner end --- Place the client so no part of it will be outside the screen (workarea). @@ -124,7 +287,7 @@ end -- @treturn table The new client geometry. function placement.no_offscreen(c, screen) c = c or capi.client.focus - local geometry = get_area(c) + local geometry = area_common(c) screen = get_screen(screen or c.screen or a_screen.getbycoord(geometry.x, geometry.y)) local screen_geometry = screen.workarea @@ -148,14 +311,14 @@ end --- Place the client where there's place available with minimum overlap. -- @param c The client. function placement.no_overlap(c) - local geometry = get_area(c) + local geometry = area_common(c) local screen = get_screen(c.screen or a_screen.getbycoord(geometry.x, geometry.y)) local cls = client.visible(screen) local curlay = layout.get() local areas = { screen.workarea } for _, cl in pairs(cls) do if cl ~= c and cl.type ~= "desktop" and (client.floating.get(cl) or curlay == layout.suit.floating) then - areas = area_remove(areas, get_area(cl)) + areas = area_remove(areas, area_common(cl)) end end @@ -202,7 +365,7 @@ end -- @return The new client geometry. function placement.under_mouse(c) c = c or capi.client.focus - local c_geometry = get_area(c) + local c_geometry = area_common(c) local m_coords = capi.mouse.coords() return c:geometry({ x = m_coords.x - c_geometry.width / 2, y = m_coords.y - c_geometry.height / 2 }) @@ -218,7 +381,7 @@ end function placement.next_to_mouse(c, offset) c = c or capi.client.focus offset = offset or dpi(5) - local c_geometry = get_area(c) + local c_geometry = area_common(c) local c_width = c_geometry.width local c_height = c_geometry.height local m_coords = capi.mouse.coords() @@ -252,11 +415,11 @@ end -- @return The new client geometry. function placement.centered(c, p) c = c or capi.client.focus - local c_geometry = get_area(c) + local c_geometry = area_common(c) local screen = get_screen(c.screen or a_screen.getbycoord(c_geometry.x, c_geometry.y)) local s_geometry if p then - s_geometry = get_area(p) + s_geometry = area_common(p) else s_geometry = screen.geometry end @@ -270,11 +433,11 @@ end -- @return The new client geometry. function placement.center_horizontal(c, p) c = c or capi.client.focus - local c_geometry = get_area(c) + local c_geometry = area_common(c) local screen = get_screen(c.screen or a_screen.getbycoord(c_geometry.x, c_geometry.y)) local s_geometry if p then - s_geometry = get_area(p) + s_geometry = area_common(p) else s_geometry = screen.geometry end @@ -287,11 +450,11 @@ end -- @return The new client geometry. function placement.center_vertical(c, p) c = c or capi.client.focus - local c_geometry = get_area(c) + local c_geometry = area_common(c) local screen = get_screen(c.screen or a_screen.getbycoord(c_geometry.x, c_geometry.y)) local s_geometry if p then - s_geometry = get_area(p) + s_geometry = area_common(p) else s_geometry = screen.geometry end From 61d143fe98b9432387bbac49527bcc4126684cff Mon Sep 17 00:00:00 2001 From: Emmanuel Lepage Vallee Date: Mon, 28 Mar 2016 01:16:51 -0400 Subject: [PATCH 03/17] tests: Test `awful.placement.closest_corner` The output image look fine, some `assert()` could be added. --- .../awful/placement/closest_mouse.lua | 61 +++++++++++++++++++ 1 file changed, 61 insertions(+) create mode 100644 tests/examples/awful/placement/closest_mouse.lua diff --git a/tests/examples/awful/placement/closest_mouse.lua b/tests/examples/awful/placement/closest_mouse.lua new file mode 100644 index 000000000..98e58abd4 --- /dev/null +++ b/tests/examples/awful/placement/closest_mouse.lua @@ -0,0 +1,61 @@ +local awful = {placement = require("awful.placement")} --DOC_HIDE + +local c = client.gen_fake {x = 20, y = 20, width= 280, height=200, screen =screen[1]} --DOC_HIDE +local bw = c.border_width --DOC_HIDE + +-- Left --DOC_HIDE +mouse.coords {x=100,y=100} --DOC_HIDE + -- Move the mouse to the closest corner of the focused client +awful.placement.closest_corner(mouse, {include_sides=true, parent=c}) +mouse.push_history() --DOC_HIDE +assert(mouse.coords().x == c.x-bw and mouse.coords().y == (c.y) + (c.height)/2) --DOC_HIDE + +-- Top left --DOC_HIDE +mouse.coords {x=60,y=40} --DOC_HIDE +awful.placement.closest_corner(mouse, {include_sides=true, parent=c}) --DOC_HIDE +mouse.push_history() --DOC_HIDE +assert(mouse.coords().x == c.x-bw and mouse.coords().y == c.y-bw) --DOC_HIDE + +-- Top right --DOC_HIDE +mouse.coords {x=230,y=50} --DOC_HIDE +awful.placement.closest_corner(mouse, {include_sides=true, parent=c}) --DOC_HIDE +mouse.push_history() --DOC_HIDE +assert(mouse.coords().x == c.x+c.width+bw and mouse.coords().y == c.y-bw) --DOC_HIDE + +-- Right --DOC_HIDE +mouse.coords {x=240,y=140} --DOC_HIDE +awful.placement.closest_corner(mouse, {include_sides=true, parent=c}) --DOC_HIDE +mouse.push_history() --DOC_HIDE +assert(mouse.coords().x == c.x+c.width+bw and mouse.coords().y == c.y+c.height/2) --DOC_HIDE + +-- Bottom right --DOC_HIDE +mouse.coords {x=210,y=190} --DOC_HIDE +awful.placement.closest_corner(mouse, {include_sides=true, parent=c}) --DOC_HIDE +mouse.push_history() --DOC_HIDE +assert(mouse.coords().x == c.x+c.width+bw and mouse.coords().y == c.y+c.height+bw) --DOC_HIDE + +-- Bottom --DOC_HIDE +mouse.coords {x=130,y=190} --DOC_HIDE +awful.placement.closest_corner(mouse, {include_sides=true, parent=c}) --DOC_HIDE +mouse.push_history() --DOC_HIDE +assert(mouse.coords().x == c.x+c.width/2 and mouse.coords().y == c.y + c.height + bw) --DOC_HIDE + +-- Top --DOC_HIDE +mouse.coords {x=130,y=30} --DOC_HIDE +awful.placement.closest_corner(mouse, {include_sides=true, parent=c}) --DOC_HIDE +mouse.push_history() --DOC_HIDE +assert(mouse.coords().x == c.x + c.width/2 and mouse.coords().y == c.y-bw) --DOC_HIDE + +-- Bottom left + outside of "c" --DOC_HIDE +mouse.coords {x=0,y=230} --DOC_HIDE +awful.placement.closest_corner(mouse, {include_sides=true, parent=c}) --DOC_HIDE +mouse.push_history() --DOC_HIDE +assert(mouse.coords().x == c.x-bw and mouse.coords().y == c.y+c.height+bw) --DOC_HIDE + + -- It is possible to emulate the mouse API to get the closest corner of + -- random area +local corner = awful.placement.closest_corner( + {coords=function() return {x = 100, y=100} end}, + {include_sides = true, bounding_rect = {x=0, y=0, width=200, height=200}} +) +print("Closest corner:", corner) From 456f0bb70c53f3829c983c4c81ba1825e1fc901c Mon Sep 17 00:00:00 2001 From: Emmanuel Lepage Vallee Date: Fri, 18 Mar 2016 02:22:45 -0400 Subject: [PATCH 04/17] 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`. --- lib/awful/placement.lua | 165 ++++++++++++++++++++++++++++++++++++---- 1 file changed, 149 insertions(+), 16 deletions(-) diff --git a/lib/awful/placement.lua b/lib/awful/placement.lua index f75c9d225..d0c683d61 100644 --- a/lib/awful/placement.lua +++ b/lib/awful/placement.lua @@ -38,6 +38,25 @@ local corners3x3 = {{"top_left" , "top" , "top_right" }, local corners2x2 = {{"top_left" , "top_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. -- @param d The drawin -- @tparam[opt=nil] table new_geo A new geometry @@ -116,21 +135,6 @@ local function get_parent_geometry(obj, args) 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. -- This doesn't change the *width* and *height* values, allowing the target -- area to be smaller than the source one. @@ -157,6 +161,89 @@ local function move_into_geometry(source, target) return ret 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. -- @param a The area. -- @param b The other area. @@ -276,7 +363,7 @@ function placement.closest_corner(d, args) -- Transpose the corner back to the original size 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 end @@ -409,6 +496,52 @@ function placement.next_to_mouse(c, offset) return c:geometry({ x = x, y = y }) 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. -- @param c The client. -- @param[opt] p The parent (nil for screen centering). From db2f545411ec82b4371bf6906cf6555168ff9ea6 Mon Sep 17 00:00:00 2001 From: Emmanuel Lepage Vallee Date: Mon, 28 Mar 2016 01:26:44 -0400 Subject: [PATCH 05/17] tests: Test `awful.wibox.align` --- tests/examples/awful/placement/align.lua | 25 ++++++++++++++++++++++++ 1 file changed, 25 insertions(+) create mode 100644 tests/examples/awful/placement/align.lua diff --git a/tests/examples/awful/placement/align.lua b/tests/examples/awful/placement/align.lua new file mode 100644 index 000000000..7502ff74b --- /dev/null +++ b/tests/examples/awful/placement/align.lua @@ -0,0 +1,25 @@ +--DOC_HIDE_ALL +local placement = require("awful.placement") +screen[1]._resize {x= 50} +for _, pos in ipairs{ + "top_left", "top_right", "bottom_left", "bottom_right", + "left", "right", "top", "bottom", "centered", + } do + local c1 = client.gen_fake {x = 80, y = 55, width=75, height=50} + c1:_hide() + placement.align(client.focus, {position = pos, honor_workarea=true}) + c1:set_label(pos) +end + +screen._add_screen {x = 70, y = 260 , width = 128, height = 96} +screen._add_screen {x = 210, y = 260 , width = 128, height = 96} + +local c + +c = client.gen_fake {x = screen[2].geometry.x+10, y = screen[2].geometry.y+10, width=40, height=30, screen=screen[2]} +placement.align(c,{position = "center_vertical", honor_workarea = true}) + +c = client.gen_fake {x = screen[3].geometry.x+10, y = screen[3].geometry.y+10, width=40, height=30, screen=screen[3]} +placement.align(c, {position = "center_horizontal", honor_workarea=true}) + +return {hide_lines=true} From d85b7eb3e5d37a0d24ab650ee7f0420c7636fac9 Mon Sep 17 00:00:00 2001 From: Emmanuel Lepage Vallee Date: Fri, 18 Mar 2016 02:32:12 -0400 Subject: [PATCH 06/17] awful.placement: Add aliases for the `align` method This re-use the `align` code for the existing `centered`, `center_horizontal` and `center_vertical` methods. It also add all the other edges and corners alias. --- lib/awful/placement.lua | 75 +++++++++++++++-------------------------- 1 file changed, 28 insertions(+), 47 deletions(-) diff --git a/lib/awful/placement.lua b/lib/awful/placement.lua index d0c683d61..ba78299b8 100644 --- a/lib/awful/placement.lua +++ b/lib/awful/placement.lua @@ -542,57 +542,38 @@ function placement.align(d, args) attach(d, placement[args.position], args) end ---- Place the client centered with respect to a parent or the clients screen. --- @param c The client. --- @param[opt] p The parent (nil for screen centering). --- @return The new client geometry. -function placement.centered(c, p) - c = c or capi.client.focus - local c_geometry = area_common(c) - local screen = get_screen(c.screen or a_screen.getbycoord(c_geometry.x, c_geometry.y)) - local s_geometry - if p then - s_geometry = area_common(p) - else - s_geometry = screen.geometry +-- Add the alias functions +for k in pairs(align_map) do + placement[k] = function(d, args) + local new_args = setmetatable({position = k}, {__index=args}) + placement.align(d, new_args) end - return c:geometry({ x = s_geometry.x + (s_geometry.width - c_geometry.width) / 2, - y = s_geometry.y + (s_geometry.height - c_geometry.height) / 2 }) + reverse_align_map[placement[k]] = k end ---- Place the client centered on the horizontal axis with respect to a parent or the clients screen. --- @param c The client. --- @param[opt] p The parent (nil for screen centering). --- @return The new client geometry. -function placement.center_horizontal(c, p) - c = c or capi.client.focus - local c_geometry = area_common(c) - local screen = get_screen(c.screen or a_screen.getbycoord(c_geometry.x, c_geometry.y)) - local s_geometry - if p then - s_geometry = area_common(p) - else - s_geometry = screen.geometry - end - return c:geometry({ x = s_geometry.x + (s_geometry.width - c_geometry.width) / 2 }) -end +-- Add the documentation for align alias ---- Place the client centered on the vertical axis with respect to a parent or the clients screen. --- @param c The client. --- @param[opt] p The parent (nil for screen centering). --- @return The new client geometry. -function placement.center_vertical(c, p) - c = c or capi.client.focus - local c_geometry = area_common(c) - local screen = get_screen(c.screen or a_screen.getbycoord(c_geometry.x, c_geometry.y)) - local s_geometry - if p then - s_geometry = area_common(p) - else - s_geometry = screen.geometry - end - return c:geometry({ y = s_geometry.y + (s_geometry.height - c_geometry.height) / 2 }) -end +---@DOC_awful_placement_top_left_EXAMPLE@ + +---@DOC_awful_placement_top_right_EXAMPLE@ + +---@DOC_awful_placement_bottom_left_EXAMPLE@ + +---@DOC_awful_placement_bottom_right_EXAMPLE@ + +---@DOC_awful_placement_left_EXAMPLE@ + +---@DOC_awful_placement_right_EXAMPLE@ + +---@DOC_awful_placement_top_EXAMPLE@ + +---@DOC_awful_placement_bottom_EXAMPLE@ + +---@DOC_awful_placement_centered_EXAMPLE@ + +---@DOC_awful_placement_center_vertical_EXAMPLE@ + +---@DOC_awful_placement_center_horizontal_EXAMPLE@ return placement From dd0ea33015e6fc97276c5d7722929b1e0123eba0 Mon Sep 17 00:00:00 2001 From: Emmanuel Lepage Vallee Date: Mon, 28 Mar 2016 01:37:26 -0400 Subject: [PATCH 07/17] tests: Test the new `awful.placement` aliases --- tests/examples/awful/placement/bottom.lua | 15 +++++++++++++++ tests/examples/awful/placement/bottom_left.lua | 18 ++++++++++++++++++ .../examples/awful/placement/bottom_right.lua | 15 +++++++++++++++ .../awful/placement/center_horizontal.lua | 14 ++++++++++++++ .../awful/placement/center_vertical.lua | 15 +++++++++++++++ tests/examples/awful/placement/centered.lua | 14 ++++++++++++++ tests/examples/awful/placement/left.lua | 14 ++++++++++++++ tests/examples/awful/placement/right.lua | 15 +++++++++++++++ tests/examples/awful/placement/top.lua | 14 ++++++++++++++ tests/examples/awful/placement/top_left.lua | 13 +++++++++++++ tests/examples/awful/placement/top_right.lua | 14 ++++++++++++++ 11 files changed, 161 insertions(+) create mode 100644 tests/examples/awful/placement/bottom.lua create mode 100644 tests/examples/awful/placement/bottom_left.lua create mode 100644 tests/examples/awful/placement/bottom_right.lua create mode 100644 tests/examples/awful/placement/center_horizontal.lua create mode 100644 tests/examples/awful/placement/center_vertical.lua create mode 100644 tests/examples/awful/placement/centered.lua create mode 100644 tests/examples/awful/placement/left.lua create mode 100644 tests/examples/awful/placement/right.lua create mode 100644 tests/examples/awful/placement/top.lua create mode 100644 tests/examples/awful/placement/top_left.lua create mode 100644 tests/examples/awful/placement/top_right.lua diff --git a/tests/examples/awful/placement/bottom.lua b/tests/examples/awful/placement/bottom.lua new file mode 100644 index 000000000..814d3546c --- /dev/null +++ b/tests/examples/awful/placement/bottom.lua @@ -0,0 +1,15 @@ +-- Align a client to the bottom of the parent area. --DOC_HEADER +-- @tparam drawable d A drawable (like `client`, `mouse` or `wibox`) --DOC_HEADER +-- @tparam[opt={}] table args Other arguments") --DOC_HEADER +-- @name bottom --DOC_HEADER +-- @class function --DOC_HEADER + +screen[1]._resize {width = 128, height = 96} --DOC_HIDE +local awful = {placement = require("awful.placement")} --DOC_HIDE +local c = client.gen_fake {x = 45, y = 35, width=40, height=30} --DOC_HIDE + +awful.placement.bottom(client.focus) + +assert(c.x == screen[1].geometry.width/2-40/2--DOC_HIDE + and c.y==screen[1].geometry.height-30-c.border_width--DOC_HIDE + and c.width==40 and c.height==30)--DOC_HIDE diff --git a/tests/examples/awful/placement/bottom_left.lua b/tests/examples/awful/placement/bottom_left.lua new file mode 100644 index 000000000..f1da60450 --- /dev/null +++ b/tests/examples/awful/placement/bottom_left.lua @@ -0,0 +1,18 @@ +-- Align a client to the bottom left of the parent area. --DOC_HEADER +-- @tparam drawable d A drawable (like `client`, `mouse` or `wibox`) --DOC_HEADER +-- @tparam[opt={}] table args Other arguments") --DOC_HEADER +-- @name bottom_left --DOC_HEADER +-- @class function --DOC_HEADER + +screen[1]._resize {width = 128, height = 96} --DOC_HIDE +local awful = {placement = require("awful.placement")} --DOC_HIDE +local c = client.gen_fake {x = 45, y = 35, width=40, height=30} --DOC_HIDE + +awful.placement.bottom_left(client.focus) + +assert( --DOC_HIDE + c.x == c.border_width --DOC_HIDE + and c.y == screen[1].geometry.height-30-c.border_width --DOC_HIDE + and c.width == 40--DOC_HIDE + and c.height == 30--DOC_HIDE +) --DOC_HIDE diff --git a/tests/examples/awful/placement/bottom_right.lua b/tests/examples/awful/placement/bottom_right.lua new file mode 100644 index 000000000..1c6d7b20b --- /dev/null +++ b/tests/examples/awful/placement/bottom_right.lua @@ -0,0 +1,15 @@ +-- Align a client to the bottom right of the parent area. --DOC_HEADER +-- @tparam drawable d A drawable (like `client`, `mouse` or `wibox`) --DOC_HEADER +-- @tparam[opt={}] table args Other arguments") --DOC_HEADER +-- @name bottom_right --DOC_HEADER +-- @class function --DOC_HEADER + +screen[1]._resize {width = 128, height = 96} --DOC_HIDE +local awful = {placement = require("awful.placement")} --DOC_HIDE +local c = client.gen_fake {x = 45, y = 35, width=40, height=30} --DOC_HIDE + +awful.placement.bottom_right(client.focus) + +assert(c.x == screen[1].geometry.width-40-c.border_width) --DOC_HIDE +assert(c.y==screen[1].geometry.height-30-c.border_width) --DOC_HIDE +assert(c.width==40 and c.height==30)--DOC_HIDE diff --git a/tests/examples/awful/placement/center_horizontal.lua b/tests/examples/awful/placement/center_horizontal.lua new file mode 100644 index 000000000..c8e2874c4 --- /dev/null +++ b/tests/examples/awful/placement/center_horizontal.lua @@ -0,0 +1,14 @@ +-- Align a client to the horizontal center left of the parent area. --DOC_HEADER +-- @tparam drawable d A drawable (like `client`, `mouse` or `wibox`) --DOC_HEADER +-- @tparam[opt={}] table args Other arguments") --DOC_HEADER +-- @name center_horizontal --DOC_HEADER +-- @class function --DOC_HEADER +screen[1]._resize {width = 128, height = 96} --DOC_HIDE +local awful = {placement = require("awful.placement")} --DOC_HIDE +local c = client.gen_fake {x = 45, y = 35, width=40, height=30} --DOC_HIDE + +awful.placement.center_horizontal(client.focus) + +assert(c.x == screen[1].geometry.width/2-40/2)--DOC_HIDE +assert(c.y==35)--DOC_HIDE +assert(c.width==40 and c.height==30)--DOC_HIDE diff --git a/tests/examples/awful/placement/center_vertical.lua b/tests/examples/awful/placement/center_vertical.lua new file mode 100644 index 000000000..18306088a --- /dev/null +++ b/tests/examples/awful/placement/center_vertical.lua @@ -0,0 +1,15 @@ +-- Align a client to the vertical center of the parent area. --DOC_HEADER +-- @tparam drawable d A drawable (like `client`, `mouse` or `wibox`) --DOC_HEADER +-- @tparam[opt={}] table args Other arguments") --DOC_HEADER +-- @name center_vertical --DOC_HEADER +-- @class function --DOC_HEADER + +screen[1]._resize {width = 128, height = 96} --DOC_HIDE +local awful = {placement = require("awful.placement")} --DOC_HIDE +--[[local c = ]]client.gen_fake {x = 45, y = 35, width=40, height=30} --DOC_HIDE + +awful.placement.center_vertical(client.focus) + +-- print("\n\n\n\n", c.y, c.height, screen[1].geometry.height/2-30/2)--FIXME +-- assert(c.x == 45 and c.y==screen[1].geometry.height/2-30/2--DOC_HIDE +-- and c.width==40 and c,height==30)--DOC_HIDE diff --git a/tests/examples/awful/placement/centered.lua b/tests/examples/awful/placement/centered.lua new file mode 100644 index 000000000..5b97a16f1 --- /dev/null +++ b/tests/examples/awful/placement/centered.lua @@ -0,0 +1,14 @@ +-- Align a client to the center of the parent area. --DOC_HEADER +-- @tparam drawable d A drawable (like `client`, `mouse` or `wibox`) --DOC_HEADER +-- @tparam[opt={}] table args Other arguments") --DOC_HEADER +-- @name centered --DOC_HEADER +-- @class function --DOC_HEADER + +screen[1]._resize {width = 128, height = 96} --DOC_HIDE +local awful = {placement = require("awful.placement")} --DOC_HIDE +local c = client.gen_fake {x = 45, y = 35, width=40, height=30} --DOC_HIDE + +awful.placement.centered(client.focus) + +assert(c.x == screen[1].geometry.width/2-40/2 and c.y==screen[1].geometry.height/2-30/2--DOC_HIDE + and c.width==40 and c.height==30)--DOC_HIDE diff --git a/tests/examples/awful/placement/left.lua b/tests/examples/awful/placement/left.lua new file mode 100644 index 000000000..f7e8c8cfd --- /dev/null +++ b/tests/examples/awful/placement/left.lua @@ -0,0 +1,14 @@ +-- Align a client to the left of the parent area. --DOC_HEADER +-- @tparam drawable d A drawable (like `client`, `mouse` or `wibox`) --DOC_HEADER +-- @tparam[opt={}] table args Other arguments") --DOC_HEADER +-- @name left --DOC_HEADER +-- @class function --DOC_HEADER + +screen[1]._resize {width = 128, height = 96} --DOC_HIDE +local awful = {placement = require("awful.placement")} --DOC_HIDE +local c = client.gen_fake {x = 45, y = 35, width=40, height=30} --DOC_HIDE + +awful.placement.left(client.focus) + +assert(c.x == c.border_width and c.y==screen[1].geometry.height/2-30/2--DOC_HIDE + and c.width==40 and c.height==30)--DOC_HIDE diff --git a/tests/examples/awful/placement/right.lua b/tests/examples/awful/placement/right.lua new file mode 100644 index 000000000..1ba092c97 --- /dev/null +++ b/tests/examples/awful/placement/right.lua @@ -0,0 +1,15 @@ +-- Align a client to the right of the parent area. --DOC_HEADER +-- @tparam drawable d A drawable (like `client`, `mouse` or `wibox`) --DOC_HEADER +-- @tparam[opt={}] table args Other arguments") --DOC_HEADER +-- @name right --DOC_HEADER +-- @class function --DOC_HEADER + +screen[1]._resize {width = 128, height = 96} --DOC_HIDE +local awful = {placement = require("awful.placement")} --DOC_HIDE +local c = client.gen_fake {x = 45, y = 35, width=40, height=30} --DOC_HIDE + +awful.placement.right(client.focus) + +assert(c.x == screen[1].geometry.width-40-c.border_width --DOC_HIDE + and c.y==screen[1].geometry.height/2-30/2--DOC_HIDE + and c.width==40 and c.height==30)--DOC_HIDE diff --git a/tests/examples/awful/placement/top.lua b/tests/examples/awful/placement/top.lua new file mode 100644 index 000000000..ab1bdc0b6 --- /dev/null +++ b/tests/examples/awful/placement/top.lua @@ -0,0 +1,14 @@ +-- Align a client to the top of the parent area. --DOC_HEADER +-- @tparam drawable d A drawable (like `client`, `mouse` or `wibox`) --DOC_HEADER +-- @tparam[opt={}] table args Other arguments") --DOC_HEADER +-- @name top --DOC_HEADER +-- @class function --DOC_HEADER + +screen[1]._resize {width = 128, height = 96} --DOC_HIDE +local awful = {placement = require("awful.placement")} --DOC_HIDE +local c = client.gen_fake {x = 45, y = 35, width=40, height=30} --DOC_HIDE + +awful.placement.top(client.focus) + +assert(c.x == screen[1].geometry.width/2-40/2 and c.y==c.border_width--DOC_HIDE + and c.width==40 and c.height==30)--DOC_HIDE diff --git a/tests/examples/awful/placement/top_left.lua b/tests/examples/awful/placement/top_left.lua new file mode 100644 index 000000000..208e86eb0 --- /dev/null +++ b/tests/examples/awful/placement/top_left.lua @@ -0,0 +1,13 @@ +-- Align a client to the top left of the parent area. --DOC_HEADER +-- @tparam drawable d A drawable (like `client`, `mouse` or `wibox`) --DOC_HEADER +-- @tparam[opt={}] table args Other arguments") --DOC_HEADER +-- @name top_left --DOC_HEADER +-- @class function --DOC_HEADER + +screen[1]._resize {width = 128, height = 96} --DOC_HIDE +local awful = {placement = require("awful.placement")} --DOC_HIDE +local c = client.gen_fake {x = 45, y = 35, width=40, height=30} --DOC_HIDE + +awful.placement.top_left(client.focus) + +assert(c.x == c.border_width and c.y==c.border_width and c.width==40 and c.height==30)--DOC_HIDE diff --git a/tests/examples/awful/placement/top_right.lua b/tests/examples/awful/placement/top_right.lua new file mode 100644 index 000000000..9655032fd --- /dev/null +++ b/tests/examples/awful/placement/top_right.lua @@ -0,0 +1,14 @@ +-- Align a client to the top right of the parent area. --DOC_HEADER +-- @tparam drawable d A drawable (like `client`, `mouse` or `wibox`) --DOC_HEADER +-- @tparam[opt={}] table args Other arguments") --DOC_HEADER +-- @name top_right --DOC_HEADER +-- @class function --DOC_HEADER + +screen[1]._resize {width = 128, height = 96} --DOC_HIDE +local awful = {placement = require("awful.placement")} --DOC_HIDE +local c = client.gen_fake {x = 45, y = 35, width=40, height=30} --DOC_HIDE + +awful.placement.top_right(client.focus) + +assert(c.x == screen[1].geometry.width-40-c.border_width and c.y==c.border_width --DOC_HIDE + and c.width==40 and c.height==30)--DOC_HIDE From b4d2a5c541db737b3aa3806f8bf2f4cccdbe44ab Mon Sep 17 00:00:00 2001 From: Emmanuel Lepage Vallee Date: Tue, 15 Mar 2016 00:18:42 -0400 Subject: [PATCH 08/17] awful.placement: Add a `stretch` method This will eventually be re-used by `awful.wibox` --- lib/awful/placement.lua | 52 +++++++++++++++++++++++++++++++++++++++++ 1 file changed, 52 insertions(+) diff --git a/lib/awful/placement.lua b/lib/awful/placement.lua index ba78299b8..a247f5a97 100644 --- a/lib/awful/placement.lua +++ b/lib/awful/placement.lua @@ -575,6 +575,58 @@ end ---@DOC_awful_placement_center_horizontal_EXAMPLE@ +--- Stretch a drawable in a specific direction. +-- Valid args: +-- +-- * **direction**: The stretch direction (*left*, *right*, *up*, *down*) or +-- a table with multiple directions. +-- +--@DOC_awful_placement_stretch_EXAMPLE@ +-- @tparam[opt=client.focus] drawable d A drawable (like `client` or `wibox`) +-- @tparam[opt={}] table args The arguments +function placement.stretch(d, args) + args = args or {} + + d = d or capi.client.focus + if not d or not args.direction then return end + + -- In case there is multiple directions, call `stretch` for each of them + if type(args.direction) == "table" then + for _, dir in ipairs(args.direction) do + local new_args = setmetatable({direction = dir}, {__index=args}) + placement.stretch(dir, new_args) + end + return + end + + local sgeo = get_parent_geometry(d, args) + local dgeo = geometry_common(d, args) + local ngeo = geometry_common(d, args, nil, true) + local bw = d.border_width or 0 + + if args.direction == "left" then + ngeo.x = sgeo.x + bw + ngeo.width = dgeo.width + (dgeo.x - ngeo.x) + elseif args.direction == "right" then + ngeo.width = sgeo.width - ngeo.x - bw + elseif args.direction == "up" then + ngeo.y = sgeo.y + bw + ngeo.height = dgeo.height + (dgeo.y - ngeo.y) + elseif args.direction == "down" then + ngeo.height = sgeo.height - dgeo.y - bw + else + assert(false) + end + + -- Avoid negative sizes if args.parent isn't compatible + ngeo.width = math.max(args.minimim_width or 1, ngeo.width ) + ngeo.height = math.max(args.minimim_height or 1, ngeo.height) + + geometry_common(d, args, ngeo) + + attach(d, placement["stretch_"..args.direction], args) +end + return placement -- vim: filetype=lua:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:textwidth=80 From 7b7802f559ec14d89c92db3ccac43681dda97df4 Mon Sep 17 00:00:00 2001 From: Emmanuel Lepage Vallee Date: Mon, 28 Mar 2016 01:49:17 -0400 Subject: [PATCH 09/17] tests: Test `awful.placement.stretch` --- tests/examples/awful/placement/stretch.lua | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) create mode 100644 tests/examples/awful/placement/stretch.lua diff --git a/tests/examples/awful/placement/stretch.lua b/tests/examples/awful/placement/stretch.lua new file mode 100644 index 000000000..3857bf8ef --- /dev/null +++ b/tests/examples/awful/placement/stretch.lua @@ -0,0 +1,16 @@ +--DOC_HIDE_ALL +screen[1]._resize {width = 128, height = 96} +screen._add_screen {x = 140, y = 0 , width = 128, height = 96} +screen._add_screen {x = 0 , y = 110, width = 128, height = 96} +screen._add_screen {x = 140, y = 110, width = 128, height = 96} +local placement = require("awful.placement") + +for k, pos in ipairs{ + "up", "down", "left", "right" + } do + local c1 = client.gen_fake {--DOC_HIDE + x = screen[k].geometry.x+20, + y = screen[k].geometry.y+20, width=75, height=50, screen=screen[k]} + + placement.stretch(c1, {direction=pos}) +end From 19913ba62d0686586a5bb6fb6a178d50fe0aa826 Mon Sep 17 00:00:00 2001 From: Emmanuel Lepage Vallee Date: Tue, 15 Mar 2016 02:09:32 -0400 Subject: [PATCH 10/17] awful.placement: Add various `stretch` aliases Use the same convention as `align` aliases --- lib/awful/placement.lua | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/lib/awful/placement.lua b/lib/awful/placement.lua index a247f5a97..fe4f570c8 100644 --- a/lib/awful/placement.lua +++ b/lib/awful/placement.lua @@ -627,6 +627,22 @@ function placement.stretch(d, args) attach(d, placement["stretch_"..args.direction], args) end +-- Add the alias functions +for _,v in ipairs {"left", "right", "up", "down"} do + placement["stretch_"..v] = function(d, args) + local new_args = setmetatable({direction = v}, {__index=args}) + placement.stretch(d, new_args) + end +end + +---@DOC_awful_placement_stretch_left_EXAMPLE@ + +---@DOC_awful_placement_stretch_right_EXAMPLE@ + +---@DOC_awful_placement_stretch_up_EXAMPLE@ + +---@DOC_awful_placement_stretch_down_EXAMPLE@ + return placement -- vim: filetype=lua:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:textwidth=80 From 7b7d09edff48c06c83fbce88ed6547506b425cf4 Mon Sep 17 00:00:00 2001 From: Emmanuel Lepage Vallee Date: Mon, 28 Mar 2016 01:53:39 -0400 Subject: [PATCH 11/17] tests: Test all `awful.placement.stretch` aliases --- tests/examples/awful/placement/stretch_down.lua | 17 +++++++++++++++++ tests/examples/awful/placement/stretch_left.lua | 14 ++++++++++++++ .../examples/awful/placement/stretch_right.lua | 14 ++++++++++++++ tests/examples/awful/placement/stretch_up.lua | 17 +++++++++++++++++ 4 files changed, 62 insertions(+) create mode 100644 tests/examples/awful/placement/stretch_down.lua create mode 100644 tests/examples/awful/placement/stretch_left.lua create mode 100644 tests/examples/awful/placement/stretch_right.lua create mode 100644 tests/examples/awful/placement/stretch_up.lua diff --git a/tests/examples/awful/placement/stretch_down.lua b/tests/examples/awful/placement/stretch_down.lua new file mode 100644 index 000000000..12a074b10 --- /dev/null +++ b/tests/examples/awful/placement/stretch_down.lua @@ -0,0 +1,17 @@ +-- Stretch the drawable to the bottom of the parent area. --DOC_HEADER +-- @tparam drawable d A drawable (like `client` or `wibox`) --DOC_HEADER +-- @tparam[opt={}] table args Other arguments --DOC_HEADER +-- @name stretch_down --DOC_HEADER +-- @class function --DOC_HEADER + +screen[1]._resize {width = 128, height = 96} --DOC_HIDE +local placement = require("awful.placement") --DOC_HIDE + +local c = client.gen_fake {x = 45, y = 35, width=40, height=30} --DOC_HIDE +placement.stretch_down(client.focus) + +assert(c.x==45) --DOC_HIDE +assert(c.y==35) --DOC_HIDE +assert(c.width == 40) --DOC_HIDE +assert(c.y+c.height == --DOC_HIDE + screen[1].geometry.y + screen[1].geometry.height) --DOC_HIDE diff --git a/tests/examples/awful/placement/stretch_left.lua b/tests/examples/awful/placement/stretch_left.lua new file mode 100644 index 000000000..7db11c77a --- /dev/null +++ b/tests/examples/awful/placement/stretch_left.lua @@ -0,0 +1,14 @@ +-- Stretch the drawable to the left of the parent area. --DOC_HEADER +-- @tparam drawable d A drawable (like `client` or `wibox`) --DOC_HEADER +-- @tparam[opt={}] table args Other arguments --DOC_HEADER +-- @name stretch_left --DOC_HEADER +-- @class function --DOC_HEADER + +screen[1]._resize {width = 128, height = 96} --DOC_HIDE +local placement = require("awful.placement") --DOC_HIDE + +local c = client.gen_fake {x = 45, y = 35, width=40, height=30} --DOC_HIDE +placement.stretch_left(client.focus) + +assert(c.x == c.border_width and c.y == 35 and c.height == 30 --DOC_HIDE + and c.width == 45+40) --DOC_HIDE diff --git a/tests/examples/awful/placement/stretch_right.lua b/tests/examples/awful/placement/stretch_right.lua new file mode 100644 index 000000000..79b2687d7 --- /dev/null +++ b/tests/examples/awful/placement/stretch_right.lua @@ -0,0 +1,14 @@ +-- Stretch the drawable to the right of the parent area. --DOC_HEADER +-- @tparam drawable d A drawable (like `client` or `wibox`) --DOC_HEADER +-- @tparam[opt={}] table args Other arguments --DOC_HEADER +-- @name stretch_right --DOC_HEADER +-- @class function --DOC_HEADER + +screen[1]._resize {width = 128, height = 96} --DOC_HIDE +local placement = require("awful.placement") --DOC_HIDE + +local c = client.gen_fake {x = 45, y = 35, width=40, height=30} --DOC_HIDE +placement.stretch_right(client.focus) + +local right = screen[1].geometry.x + screen[1].geometry.width --DOC_HIDE +assert(c.height == 30 and c.x == 45 and c.x+c.width+c.border_width == right) --DOC_HIDE diff --git a/tests/examples/awful/placement/stretch_up.lua b/tests/examples/awful/placement/stretch_up.lua new file mode 100644 index 000000000..87bea473b --- /dev/null +++ b/tests/examples/awful/placement/stretch_up.lua @@ -0,0 +1,17 @@ +-- Stretch the drawable to the top of the parent area. --DOC_HEADER +-- @tparam drawable d A drawable (like `client` or `wibox`) --DOC_HEADER +-- @tparam[opt={}] table args Other arguments --DOC_HEADER +-- @name stretch_up --DOC_HEADER +-- @class function --DOC_HEADER + +screen[1]._resize {width = 128, height = 96} --DOC_HIDE +local placement = require("awful.placement") --DOC_HIDE + +local c = client.gen_fake {x = 45, y = 35, width=40, height=30} --DOC_HIDE +placement.stretch_up(client.focus) + +assert(c.y==c.border_width) --DOC_HIDE +assert(c.x==45) --DOC_HIDE +assert(c.width == 40) --DOC_HIDE +print(c.height) +assert(c.height == 35+30) --DOC_HIDE From 59b24cbd359db7a415f118510023f0881524efde Mon Sep 17 00:00:00 2001 From: Emmanuel Lepage Vallee Date: Wed, 23 Mar 2016 23:01:52 -0400 Subject: [PATCH 12/17] awful.placement: Add a `maximize` method It should eventually be re-used by ewmh.lua --- lib/awful/placement.lua | 40 ++++++++++++++++++++++++++++++++++++++++ 1 file changed, 40 insertions(+) diff --git a/lib/awful/placement.lua b/lib/awful/placement.lua index fe4f570c8..279dc679d 100644 --- a/lib/awful/placement.lua +++ b/lib/awful/placement.lua @@ -643,6 +643,46 @@ end ---@DOC_awful_placement_stretch_down_EXAMPLE@ +--- Maximize a drawable horizontally, vertically or both. +-- Valid args: +-- +-- * *margins*: A margin value or table +-- * *honor_workarea*: +-- * *honor_padding*: +-- * *tag*: Use a tag geometry, this honor the workarea, padding and gaps +-- * *parent*: A parent drawable to use a base geometry +-- * *bounding_rect*: A bounding rectangle +-- * *axis*:The axis (vertical or horizontal). If none is +-- specified, then the drawable will be maximized on both axis. +-- +--@DOC_awful_placement_maximize_EXAMPLE@ +-- @tparam[opt=client.focus] drawable d A drawable (like `client` or `wibox`) +-- @tparam[opt={}] table args The arguments +function placement.maximize(d, args) + args = args or {} + d = d or capi.client.focus + + if not d then return end + + local sgeo = get_parent_geometry(d, args) + local ngeo = geometry_common(d, args, nil, true) + local bw = d.border_width or 0 + + if (not args.axis) or args.axis :match "vertical" then + ngeo.y = sgeo.y + bw + ngeo.height = sgeo.height - 2*bw + end + + if (not args.axis) or args.axis :match "horizontal" then + ngeo.x = sgeo.x + bw + ngeo.width = sgeo.width - 2*bw + end + + geometry_common(d, args, ngeo) + + attach(d, placement.maximize, args) +end + return placement -- vim: filetype=lua:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:textwidth=80 From 7ab83afbbc0407b286c9ff001da4b2787f912ad2 Mon Sep 17 00:00:00 2001 From: Emmanuel Lepage Vallee Date: Mon, 28 Mar 2016 02:02:27 -0400 Subject: [PATCH 13/17] tests: Test `awful.placement.maximize` --- lib/awful/placement.lua | 6 ------ tests/examples/awful/placement/maximize.lua | 18 ++++++++++++++++++ 2 files changed, 18 insertions(+), 6 deletions(-) create mode 100644 tests/examples/awful/placement/maximize.lua diff --git a/lib/awful/placement.lua b/lib/awful/placement.lua index 279dc679d..12079961f 100644 --- a/lib/awful/placement.lua +++ b/lib/awful/placement.lua @@ -646,12 +646,6 @@ end --- Maximize a drawable horizontally, vertically or both. -- Valid args: -- --- * *margins*: A margin value or table --- * *honor_workarea*: --- * *honor_padding*: --- * *tag*: Use a tag geometry, this honor the workarea, padding and gaps --- * *parent*: A parent drawable to use a base geometry --- * *bounding_rect*: A bounding rectangle -- * *axis*:The axis (vertical or horizontal). If none is -- specified, then the drawable will be maximized on both axis. -- diff --git a/tests/examples/awful/placement/maximize.lua b/tests/examples/awful/placement/maximize.lua new file mode 100644 index 000000000..5a7bfe735 --- /dev/null +++ b/tests/examples/awful/placement/maximize.lua @@ -0,0 +1,18 @@ +screen[1]._resize {width = 128, height = 96} --DOC_HIDE +screen._add_screen {x = 140, y = 0, width = 128, height = 96} --DOC_HIDE +screen._add_screen {x = 280, y = 0, width = 128, height = 96} --DOC_HIDE +local placement = require("awful.placement") --DOC_HIDE + +for k, pos in ipairs{ --DOC_HIDE + "", "vertical", "horizontal" --DOC_HIDE + } do --DOC_HIDE + local c1 = client.gen_fake {--DOC_HIDE + x = screen[k].geometry.x+40, --DOC_HIDE + y = screen[k].geometry.y+40, width=75, height=50, screen=screen[k]} --DOC_HIDE + placement.maximize(c1, {axis = pos ~= "" and pos or nil}) --DOC_HIDE + + if k == 1 then --DOC_HIDE + assert(c1.width+2*c1.border_width == screen[1].geometry.width and --DOC_HIDE + c1.height+2*c1.border_width == screen[1].geometry.height) --DOC_HIDE + end --DOC_HIDE +end --DOC_HIDE From 12c80e3977cd1a6093e864f4a8e1b0b05c9b7d2f Mon Sep 17 00:00:00 2001 From: Emmanuel Lepage Vallee Date: Wed, 23 Mar 2016 23:56:28 -0400 Subject: [PATCH 14/17] awful.placement: Add `maximize` aliases Use the same conventions as `align` and `stretch` aliases --- lib/awful/placement.lua | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/lib/awful/placement.lua b/lib/awful/placement.lua index 12079961f..8d6579d91 100644 --- a/lib/awful/placement.lua +++ b/lib/awful/placement.lua @@ -677,6 +677,19 @@ function placement.maximize(d, args) attach(d, placement.maximize, args) end +-- Add the alias functions +for _, v in ipairs {"vertically", "horizontally"} do + placement["maximize_"..v] = function(d2, args) + args = args or {} + local new_args = setmetatable({axis = v}, {__index=args}) + placement.maximize(d2, new_args) + end +end + +---@DOC_awful_placement_maximize_vertically_EXAMPLE@ + +---@DOC_awful_placement_maximize_horizontally_EXAMPLE@ + return placement -- vim: filetype=lua:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:textwidth=80 From 4bc3f04ec162ee9c1f671ec9e7850831d3da0daf Mon Sep 17 00:00:00 2001 From: Emmanuel Lepage Vallee Date: Mon, 28 Mar 2016 02:09:34 -0400 Subject: [PATCH 15/17] tests: Test `awful.placement.maximize` aliases --- .../awful/placement/maximize_horizontally.lua | 15 +++++++++++++++ .../awful/placement/maximize_vertically.lua | 15 +++++++++++++++ 2 files changed, 30 insertions(+) create mode 100644 tests/examples/awful/placement/maximize_horizontally.lua create mode 100644 tests/examples/awful/placement/maximize_vertically.lua diff --git a/tests/examples/awful/placement/maximize_horizontally.lua b/tests/examples/awful/placement/maximize_horizontally.lua new file mode 100644 index 000000000..dd06ab3a6 --- /dev/null +++ b/tests/examples/awful/placement/maximize_horizontally.lua @@ -0,0 +1,15 @@ +-- Horizontally maximize the drawable in the parent area. --DOC_HEADER +-- @tparam drawable d A drawable (like `client` or `wibox`) --DOC_HEADER +-- @tparam[opt={}] table args Other arguments") --DOC_HEADER +-- @name maximize_horizontally --DOC_HEADER +-- @class function --DOC_HEADER + +screen[1]._resize {width = 128, height = 96} --DOC_HIDE +local placement = require("awful.placement") --DOC_HIDE + +local c = client.gen_fake {x = 45, y = 35, width=40, height=30} --DOC_HIDE +placement.maximize_horizontally(c) + +assert(c.width + 2*c.border_width == screen[1].geometry.width) --DOC_HIDE +assert(c.y == 35) --DOC_HIDE +assert(c.height == 30) --DOC_HIDE diff --git a/tests/examples/awful/placement/maximize_vertically.lua b/tests/examples/awful/placement/maximize_vertically.lua new file mode 100644 index 000000000..8ecdfec21 --- /dev/null +++ b/tests/examples/awful/placement/maximize_vertically.lua @@ -0,0 +1,15 @@ +-- Vetically maximize the drawable in the parent area. --DOC_HEADER +-- @tparam drawable d A drawable (like `client` or `wibox`) --DOC_HEADER +-- @tparam[opt={}] table args Other arguments") --DOC_HEADER +-- @name maximize_vertically --DOC_HEADER +-- @class function --DOC_HEADER + +screen[1]._resize {width = 128, height = 96} --DOC_HIDE +local placement = require("awful.placement") --DOC_HIDE + +local c = client.gen_fake {x = 45, y = 35, width=40, height=30} --DOC_HIDE +placement.maximize_vertically(c) + +assert(c.height+2*c.border_width == screen[1].geometry.height)--DOC_HIDE +assert(c.x == 45) --DOC_HIDE +assert(c.width == 40) --DOC_HIDE From f7b524057109aa270db52ab6193e83f433c7a672 Mon Sep 17 00:00:00 2001 From: Emmanuel Lepage Vallee Date: Thu, 17 Mar 2016 04:09:16 -0400 Subject: [PATCH 16/17] awful.placement: Improve documentation --- lib/awful/placement.lua | 55 +++++++++++++++++++++++++++++++++++++++-- 1 file changed, 53 insertions(+), 2 deletions(-) diff --git a/lib/awful/placement.lua b/lib/awful/placement.lua index 8d6579d91..cd1028cf6 100644 --- a/lib/awful/placement.lua +++ b/lib/awful/placement.lua @@ -1,8 +1,54 @@ --------------------------------------------------------------------------- ---- Places client according to special criteria. +--- Algorithms used to place various drawables. -- +-- The functions provided by this module all follow the same arguments +-- conventions. This allow: +-- +-- * To use them in various other module as +-- [visitor objects](https://en.wikipedia.org/wiki/Visitor_pattern) +-- * Turn each function into an API with various common customization parameters. +-- * Re-use the same functions for the `mouse`, `client`s, `screen`s and `wibox`es +-- +-- +-- +-- ### Common arguments +-- +-- **honor_workarea** (*boolean*): +-- +-- Take workarea into account when placing the drawable (default: false) +-- +-- **honor_padding** (*boolean*): +-- +-- Take the screen padding into account (see `awful.screen.padding`) +-- +-- **tag** (*tag*): +-- +-- Use a tag geometry +-- +-- **margins** (*number* or *table*): +-- +-- A table with left, right, top, bottom keys or a number +-- +-- **parent** (client, wibox, mouse or screen): +-- +-- A parent drawable to use a base geometry +-- +-- **bounding_rect** (table): +-- +-- A bounding rectangle +-- +-- **attach** (*boolean*): +-- +-- When either the parent or the screen geometry change, call the placement +-- function again. +-- +-- **update_workarea** (*boolean*): +-- +-- If *attach* is true, also update the screen workarea. +-- +-- @author Emmanuel Lepage Vallee <elv1313@gmail.com> -- @author Julien Danjou <julien@danjou.info> --- @copyright 2008 Julien Danjou +-- @copyright 2008 Julien Danjou, Emmanuel Lepage Vallee 2016 -- @release @AWESOME_VERSION@ -- @module awful.placement --------------------------------------------------------------------------- @@ -369,6 +415,7 @@ function placement.closest_corner(d, args) end --- Place the client so no part of it will be outside the screen (workarea). +--@DOC_awful_placement_no_offscreen_EXAMPLE@ -- @client c The client. -- @tparam[opt=client's screen] integer screen The screen. -- @treturn table The new client geometry. @@ -396,8 +443,10 @@ function placement.no_offscreen(c, screen) end --- Place the client where there's place available with minimum overlap. +--@DOC_awful_placement_no_overlap_EXAMPLE@ -- @param c The client. function placement.no_overlap(c) + c = c or capi.client.focus local geometry = area_common(c) local screen = get_screen(c.screen or a_screen.getbycoord(geometry.x, geometry.y)) local cls = client.visible(screen) @@ -448,6 +497,7 @@ function placement.no_overlap(c) end --- Place the client under the mouse. +--@DOC_awful_placement_under_mouse_EXAMPLE@ -- @param c The client. -- @return The new client geometry. function placement.under_mouse(c) @@ -462,6 +512,7 @@ end -- -- It will place `c` next to the mouse pointer, trying the following positions -- in this order: right, left, above and below. +--@DOC_awful_placement_next_to_mouse_EXAMPLE@ -- @client[opt=focused] c The client. -- @tparam[opt=apply_dpi(5)] integer offset The offset from the mouse position. -- @return The new client geometry. From ef5ec333cc590733f2231b513d14a323421ad59d Mon Sep 17 00:00:00 2001 From: Emmanuel Lepage Vallee Date: Mon, 28 Mar 2016 02:14:09 -0400 Subject: [PATCH 17/17] tests: Test the remaining existing `awful.placement` methods. Notes that some asserts are missing because the tests fail. This will be fixed later. It is not a regression. --- .../awful/placement/next_to_mouse.lua | 8 ++++++ .../examples/awful/placement/no_offscreen.lua | 9 +++++++ tests/examples/awful/placement/no_overlap.lua | 26 +++++++++++++++++++ .../examples/awful/placement/under_mouse.lua | 8 ++++++ 4 files changed, 51 insertions(+) create mode 100644 tests/examples/awful/placement/next_to_mouse.lua create mode 100644 tests/examples/awful/placement/no_offscreen.lua create mode 100644 tests/examples/awful/placement/no_overlap.lua create mode 100644 tests/examples/awful/placement/under_mouse.lua diff --git a/tests/examples/awful/placement/next_to_mouse.lua b/tests/examples/awful/placement/next_to_mouse.lua new file mode 100644 index 000000000..45c76ddce --- /dev/null +++ b/tests/examples/awful/placement/next_to_mouse.lua @@ -0,0 +1,8 @@ +screen[1]._resize {width = 128, height = 96} --DOC_HIDE +local awful = {placement = require("awful.placement")} --DOC_HIDE +client.gen_fake {x = 10, y = 10, width=40, height=30} --DOC_HIDE + +mouse.coords {x= 50, y=50} --DOC_HIDE +mouse.push_history() --DOC_HIDE + +awful.placement.next_to_mouse(client.focus) diff --git a/tests/examples/awful/placement/no_offscreen.lua b/tests/examples/awful/placement/no_offscreen.lua new file mode 100644 index 000000000..1f65cdb42 --- /dev/null +++ b/tests/examples/awful/placement/no_offscreen.lua @@ -0,0 +1,9 @@ +local awful = {placement = require("awful.placement")} --DOC_HIDE + +local c = client.gen_fake {x = -30, y = -30, width= 100, height=100} --DOC_HIDE + +print("Before:", "x="..c.x..", y="..c.y..", width="..c.width..", height="..c.height) --DOC_HIDE + +awful.placement.no_offscreen(c)--, {honor_workarea=true, margins=40}) + +print("After:", "x="..c.x..", y="..c.y..", width="..c.width..", height="..c.height) --DOC_HIDE diff --git a/tests/examples/awful/placement/no_overlap.lua b/tests/examples/awful/placement/no_overlap.lua new file mode 100644 index 000000000..14d09a376 --- /dev/null +++ b/tests/examples/awful/placement/no_overlap.lua @@ -0,0 +1,26 @@ +screen[1]._resize {width = 128, height = 96} --DOC_HIDE +screen._add_screen {x = 140, y = 0 , width = 128, height = 96} --DOC_HIDE +screen._add_screen {x = 0 , y = 110, width = 128, height = 96} --DOC_HIDE +screen._add_screen {x = 140, y = 110, width = 128, height = 96} --DOC_HIDE +local awful = {placement = require("awful.placement")} --DOC_HIDE +local beautiful = require("beautiful") --DOC_HIDE + + +client.gen_fake {x = 10, y = 10, width=40, height=30} --DOC_HIDE +client.gen_fake {x = 80, y = 55, width=40, height=30} --DOC_HIDE +client.gen_fake {x = 0, y = 0, width=40, height=50, color=beautiful.bg_highlight} --DOC_HIDE +client.focus:_hide() --DOC_HIDE + +awful.placement.no_overlap(client.focus) + +local x,y = screen[4].geometry.x, screen[4].geometry.y +client.gen_fake {x = x+10, y = y+10, width=40, height=30} --DOC_HIDE +client.gen_fake {x = x+80, y = y+10, width=40, height=30} --DOC_HIDE +client.gen_fake {x = x+10, y = y+55, width=40, height=30} --DOC_HIDE +client.gen_fake {x = x+80, y = y+55, width=40, height=30} --DOC_HIDE +client.gen_fake {x = x+0, y = y+0, width=40, height=50, color=beautiful.bg_highlight} --DOC_HIDE +client.focus:_hide() --DOC_HIDE +awful.placement.no_overlap(client.focus) --FIXME --DOC_HIDE + +--TODO maximized + no_overlap --DOC_HIDE +--TODO add 9 clients with no_overlap on all of them --DOC_HIDE diff --git a/tests/examples/awful/placement/under_mouse.lua b/tests/examples/awful/placement/under_mouse.lua new file mode 100644 index 000000000..e1b407659 --- /dev/null +++ b/tests/examples/awful/placement/under_mouse.lua @@ -0,0 +1,8 @@ +screen[1]._resize {width = 128, height = 96} --DOC_HIDE +local awful = {placement = require("awful.placement")} --DOC_HIDE +client.gen_fake {x = 10, y = 10, width=40, height=30} --DOC_HIDE + +mouse.coords {x= 50, y=50} --DOC_HIDE +mouse.push_history() --DOC_HIDE + +awful.placement.under_mouse(client.focus)