diff options
Diffstat (limited to 'awesome/lib/awful/placement.lua')
-rw-r--r-- | awesome/lib/awful/placement.lua | 1694 |
1 files changed, 0 insertions, 1694 deletions
diff --git a/awesome/lib/awful/placement.lua b/awesome/lib/awful/placement.lua deleted file mode 100644 index d288f49..0000000 --- a/awesome/lib/awful/placement.lua +++ /dev/null @@ -1,1694 +0,0 @@ ---------------------------------------------------------------------------- ---- 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 --- --- --- <h3>Compositing</h3> --- --- It is possible to compose placement function using the `+` or `*` operator: --- --- --- ---![Usage example](../images/AUTOGEN_awful_placement_compose.svg) --- --- --- -- 'right' will be replaced by 'left' --- local f = (awful.placement.right + awful.placement.left) --- f(client.focus) --- --- --- ---![Usage example](../images/AUTOGEN_awful_placement_compose2.svg) --- --- --- -- Simulate Windows 7 'edge snap' (also called aero snap) feature --- local axis = 'vertically' --- local f = awful.placement.scale --- + awful.placement.left --- + (axis and awful.placement['maximize_'..axis] or nil) --- local geo = f(client.focus, {honor_workarea=true, to_percent = 0.5}) --- --- <h3>Common arguments</h3> --- --- **pretend** (*boolean*): --- --- Do not apply the new geometry. This is useful if only the return values is --- necessary. --- --- **honor_workarea** (*boolean*): --- --- Take workarea into account when placing the drawable (default: false) --- --- **honor_padding** (*boolean*): --- --- Take the screen padding into account (see `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 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*): --- --- The offset(s) to apply to the new geometry. --- --- **store_geometry** (*boolean*): --- --- Keep a single history of each type of placement. It can be restored using --- `awful.placement.restore` by setting the right `context` argument. --- --- 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, Emmanuel Lepage Vallee 2016 --- @module awful.placement ---------------------------------------------------------------------------- - --- Grab environment we need -local ipairs = ipairs -local pairs = pairs -local math = math -local table = table -local capi = -{ - screen = screen, - mouse = mouse, - client = client -} -local client = require("awful.client") -local layout = require("awful.layout") -local a_screen = require("awful.screen") -local grect = require("gears.geometry").rectangle -local util = require("awful.util") -local cairo = require( "lgi" ).cairo -local unpack = unpack or table.unpack -- luacheck: globals unpack (compatibility with Lua 5.1) - -local function get_screen(s) - return s and capi.screen[s] -end - -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. --- This also allow the functions to be aware they are being chained and act --- upon the previous nodes results to avoid unnecessary processing or deduce --- extra paramaters/arguments. -local function compose(...) - local queue = {} - - local nodes = {...} - - -- Allow placement.foo + (var == 42 and placement.bar) - if not nodes[2] then - return nodes[1] - end - - -- nodes[1] == self, nodes[2] == other - for _, w in ipairs(nodes) do - -- Build an execution queue - if w.context and w.context == "compose" then - for _, elem in ipairs(w.queue or {}) do - table.insert(queue, elem) - end - else - table.insert(queue, w) - end - end - - local ret - ret = wrap_client(function(d, args, ...) - local rets = {} - local last_geo = nil - - -- As some functions may have to take into account results from - -- previously execued ones, add the `composition_results` hint. - args = setmetatable({composition_results=rets}, {__index=args}) - - -- Only apply the geometry once, not once per chain node, to do this, - -- Force the "pretend" argument and restore the original value for - -- the last node. - local attach_real = args.attach - args.pretend = true - args.attach = false - args.offset = {} - - for k, f in ipairs(queue) do - if k == #queue then - -- Let them fallback to the parent table - args.pretend = nil - args.offset = nil - end - - local r = {f(d, args, ...)} - last_geo = r[1] or last_geo - args.override_geometry = last_geo - - -- Keep the return value, store one per context - if f.context then - -- When 2 composition queue are executed, merge the return values - if f.context == "compose" then - for k2,v in pairs(r) do - rets[k2] = v - end - else - rets[f.context] = r - end - end - end - - if attach_real then - args.attach = true - attach(d, ret, args) - end - - return last_geo, rets - end, "compose") - - ret.queue = queue - - return ret -end - -wrap_client = function(f, context) - return setmetatable( - { - is_placement= true, - context = context, - }, - { - __call = function(_,...) return f(...) end, - __add = compose, -- Composition is usually defined as + - __mul = compose -- Make sense if you think of the functions as matrices - } - ) -end - -local placement_private = {} - --- The module is a proxy in front of the "real" functions. --- This allow syntax like: --- --- (awful.placement.no_overlap + awful.placement.no_offscreen)(c) --- -placement = setmetatable({}, { - __index = placement_private, - __newindex = function(_, k, f) - placement_private[k] = wrap_client(f, k) - end -}) - --- 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"}} - --- 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, -} - --- Some parameters to correctly compute the final size -local resize_to_point_map = { - -- Corners - top_left = {p1= nil , p2={1,1}, x_only=false, y_only=false, align="bottom_right"}, - top_right = {p1={0,1} , p2= nil , x_only=false, y_only=false, align="bottom_left" }, - bottom_left = {p1= nil , p2={1,0}, x_only=false, y_only=false, align="top_right" }, - bottom_right = {p1={0,0} , p2= nil , x_only=false, y_only=false, align="top_left" }, - - -- Sides - left = {p1= nil , p2={1,1}, x_only=true , y_only=false, align="top_right" }, - right = {p1={0,0} , p2= nil , x_only=true , y_only=false, align="top_left" }, - top = {p1= nil , p2={1,1}, x_only=false, y_only=true , align="bottom_left" }, - bottom = {p1={0,0} , p2= nil , x_only=false, y_only=true , align="top_left" }, -} - --- Outer position matrix --- 1=best case, 2=fallback -local outer_positions = { - left1 = function(r, w, _) return {x=r.x-w , y=r.y }, "down" end, - left2 = function(r, w, h) return {x=r.x-w , y=r.y-h+r.height }, "up" end, - right1 = function(r, _, _) return {x=r.x , y=r.y }, "down" end, - right2 = function(r, _, h) return {x=r.x , y=r.y-h+r.height }, "up" end, - top1 = function(r, _, h) return {x=r.x , y=r.y-h }, "right" end, - top2 = function(r, w, h) return {x=r.x-w+r.width, y=r.y-h }, "left" end, - bottom1 = function(r, _, _) return {x=r.x , y=r.y }, "right" end, - bottom2 = function(r, w, _) return {x=r.x-w+r.width, y=r.y }, "left" end, -} - ---- Add a context to the arguments. --- This function extend the argument table. The context is used by some --- internal helper methods. If there already is a context, it has priority and --- is kept. -local function add_context(args, context) - return setmetatable({context = (args or {}).context or context }, {__index=args}) -end - -local data = setmetatable({}, { __mode = 'k' }) - ---- Store a drawable geometry (per context) in a weak table. --- @param d The drawin --- @tparam string reqtype The context. -local function store_geometry(d, reqtype) - if not data[d] then data[d] = {} end - if not data[d][reqtype] then data[d][reqtype] = {} end - data[d][reqtype] = d:geometry() - data[d][reqtype].screen = d.screen - data[d][reqtype].border_width = d.border_width -end - ---- Get the margins and offset --- @tparam table args The arguments --- @treturn table The margins --- @treturn table The offsets -local function get_decoration(args) - local offset = args.offset - - -- Offset are "blind" values added to the output - offset = type(offset) == "number" and { - x = offset, - y = offset, - width = 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 - ---- 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 { - 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) + (m.top or 0) ), - width = new_geo.width and math.max( - 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 - --- 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 --- @tparam table args the method arguments --- @treturn The drawin's area. -area_common = function(d, new_geo, ignore_border_width, args) - -- 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 - - -- When using the placement composition along with the "pretend" - -- option, it is necessary to keep a "virtual" geometry. - if args and args.override_geometry then - geometry = util.table.clone(args.override_geometry) - end - - 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) - -- Store the current geometry in a singleton-memento - if args.store_geometry and new_geo and args.context then - store_geometry(obj, args.context) - end - - -- It's a mouse - if obj.coords then - local coords = fix_new_geometry(new_geo, args) - 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, fix_new_geometry(new_geo, args), ignore_border_width, args - ) - - -- Apply the margins - if args.margins then - local delta = get_decoration(args) - - 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 obj:get_bounding_geometry(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) - -- Didable override_geometry, context and other to avoid mutating the state - -- or using the wrong geo. - - if args.bounding_rect then - return args.bounding_rect - elseif args.parent then - return geometry_common(args.parent, {}) - elseif obj.screen then - return geometry_common(obj.screen, { - honor_padding = args.honor_padding, - honor_workarea = args.honor_workarea - }) - else - return geometry_common(capi.screen[capi.mouse.screen], args) - end -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 - --- Update the workarea -wibox_update_strut = function(d, position, args) - -- 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 } - - local m = get_decoration(args) - - if vertical then - for _, v in ipairs {"right", "left"} do - if (not position) or position:match(v) then - struts[v] = geo.width + m[v] - end - end - else - for _, v in ipairs {"top", "bottom"} do - if (not position) or position:match(v) then - struts[v] = geo.height + m[v] - 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 -attach = function(d, position_f, args) - args = args or {} - - if args.pretend then return end - - if not args.attach then return end - - -- Avoid a connection loop - args = setmetatable({attach=false}, {__index=args}) - - 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 - - -- 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() - position_f(d, args) - end - - d:connect_signal("property::width" , tracker) - d:connect_signal("property::height" , tracker) - d:connect_signal("property::border_width", tracker) - - local function tracker_struts() - --TODO this is too fragile and doesn't work with all methods. - wibox_update_strut(d, d.position or reverse_align_map[position_f], args) - end - - local parent = args.parent or d.screen - - if args.update_workarea then - d:connect_signal("property::geometry" , tracker_struts) - d:connect_signal("property::visible" , tracker_struts) - capi.client.connect_signal("property::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 - - -- If there is a parent drawable, screen, also track it. - -- Note that tracking the mouse is not supported - if parent and parent.connect_signal then - parent:connect_signal("property::geometry" , tracker) - end - - -- Create a way to detach a placement function - function d.detach_callback() - d:disconnect_signal("property::width" , tracker) - d:disconnect_signal("property::height" , tracker) - d:disconnect_signal("property::border_width", tracker) - if parent then - parent:disconnect_signal("property::geometry" , tracker) - - if parent == d.screen then - if args.honor_workarea then - parent:disconnect_signal("property::workarea", tracker) - end - - if args.honor_padding then - parent:disconnect_signal("property::padding", tracker) - end - end - end - - 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 - --- Convert 2 points into a rectangle -local function rect_from_points(p1x, p1y, p2x, p2y) - return { - x = p1x, - y = p1y, - width = p2x - p1x, - height = p2y - p1y, - } -end - --- Convert a rectangle and matrix info into a point -local function rect_to_point(rect, corner_i, corner_j) - return { - x = rect.x + corner_i * math.floor(rect.width ), - y = rect.y + corner_j * math.floor(rect.height), - } -end - --- Create a pair of rectangles used to set the relative areas. --- v=vertical, h=horizontal -local function get_cross_sections(abs_geo, mode) - if not mode or mode == "cursor" then - -- A 1px cross section centered around the mouse position - local coords = capi.mouse.coords() - return { - h = { - x = abs_geo.drawable_geo.x , - y = coords.y , - width = abs_geo.drawable_geo.width , - height = 1 , - }, - v = { - x = coords.x , - y = abs_geo.drawable_geo.y , - width = 1 , - height = abs_geo.drawable_geo.height, - } - } - elseif mode == "geometry" then - -- The widget geometry extended to reach the end of the drawable - - return { - h = { - x = abs_geo.drawable_geo.x , - y = abs_geo.y , - width = abs_geo.drawable_geo.width , - height = abs_geo.height , - }, - v = { - x = abs_geo.x , - y = abs_geo.drawable_geo.y , - width = abs_geo.width , - height = abs_geo.drawable_geo.height, - } - } - elseif mode == "cursor_inside" then - -- A 1x1 rectangle centered around the mouse position - - local coords = capi.mouse.coords() - coords.width,coords.height = 1,1 - return {h=coords, v=coords} - elseif mode == "geometry_inside" then - -- The widget absolute geometry, unchanged - - return {h=abs_geo, v=abs_geo} - end -end - --- When a rectangle is embedded into a bigger one, get the regions around --- the outline of the bigger rectangle closest to the smaller one (on each side) -local function get_relative_regions(geo, mode, is_absolute) - - -- Use the mouse position and the wibox/client under it - if not geo then - local draw = capi.mouse.current_wibox - geo = draw and draw:geometry() or capi.mouse.coords() - geo.drawable = draw - elseif is_absolute then - -- Some signals are a bit inconsistent in their arguments convention. - -- This little hack tries to mitigate the issue. - - geo.drawable = geo -- is a wibox or client, geometry and object are one - -- and the same. - elseif (not geo.drawable) and geo.x and geo.width then - local coords = capi.mouse.coords() - - -- Check if the mouse is in the rect - if coords.x > geo.x and coords.x < geo.x+geo.width and - coords.y > geo.y and coords.y < geo.y+geo.height then - geo.drawable = capi.mouse.current_wibox - end - - -- Maybe there is a client - if (not geo.drawable) and capi.mouse.current_client then - geo.drawable = capi.mouse.current_client - end - end - - -- Get the drawable geometry - local dpos = geo.drawable and ( - geo.drawable.drawable and - geo.drawable.drawable:geometry() - or geo.drawable:geometry() - ) or {x=0, y=0} - - -- Compute the absolute widget geometry - local abs_widget_geo = is_absolute and geo or { - x = dpos.x + geo.x , - y = dpos.y + geo.y , - width = geo.width , - height = geo.height , - drawable = geo.drawable , - } - - abs_widget_geo.drawable_geo = geo.drawable and dpos or geo - - -- Get the point for comparison. - local center_point = mode:match("cursor") and capi.mouse.coords() or { - x = abs_widget_geo.x + abs_widget_geo.width / 2, - y = abs_widget_geo.y + abs_widget_geo.height / 2, - } - - -- Get widget regions for both axis - local cs = get_cross_sections(abs_widget_geo, mode) - - -- Get the 4 closest points from `center_point` around the wibox - local regions = { - left = {x = cs.h.x , y = cs.h.y }, - right = {x = cs.h.x+cs.h.width, y = cs.h.y }, - top = {x = cs.v.x , y = cs.v.y }, - bottom = {x = cs.v.x , y = cs.v.y+cs.v.height}, - } - - -- Assume the section is part of a single screen until someone complains. - -- It is much faster to compute and getting it wrong probably has no side - -- effects. - local s = geo.drawable and geo.drawable.screen or a_screen.getbycoord( - center_point.x, - center_point.y - ) - - -- Compute the distance (dp) between the `center_point` and the sides. - -- This is only relevant for "cursor" and "cursor_inside" modes. - for _, v in pairs(regions) do - local dx, dy = v.x - center_point.x, v.y - center_point.y - - v.distance = math.sqrt(dx*dx + dy*dy) - v.width = cs.v.width - v.height = cs.h.height - v.screen = capi.screen[s] - end - - return regions -end - --- Check if the proposed geometry fits the screen -local function fit_in_bounding(obj, geo, args) - local sgeo = get_parent_geometry(obj, args) - local region = cairo.Region.create_rectangle(cairo.RectangleInt(sgeo)) - - region:intersect(cairo.Region.create_rectangle( - cairo.RectangleInt(geo) - )) - - local geo2 = region:get_rectangle(0) - - -- If the geometry is the same then it fits, otherwise it will be cropped. - return geo2.width == geo.width and geo2.height == geo.height -end - ---- 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 --- --- --- ---![Usage example](../images/AUTOGEN_awful_placement_closest_mouse.svg) --- ---**Usage example output**: --- --- Closest corner: top_left --- --- --- @usage --- -- Move the mouse to the closest corner of the focused client ---awful.placement.closest_corner(mouse, {include_sides=true, parent=c}) --- -- 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) --- @tparam[opt=client.focus] drawable d A drawable (like `client`, `mouse` --- or `wibox`) --- @tparam[opt={}] table args The arguments --- @treturn table The new geometry --- @treturn string The corner name -function placement.closest_corner(d, args) - args = add_context(args, "closest_corner") - 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 - -- The +1 is required to avoid a rounding error when - -- pos.x == sgeo.x+sgeo.width - corner_i = -math.ceil( ( (sgeo.x - pos.x) * n) / (sgeo.width + 1)) - corner_j = -math.ceil( ( (sgeo.y - pos.y) * n) / (sgeo.height + 1)) - 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 = grid_size == 3 and f(3, corners3x3) or f(2, corners2x2) - - -- Transpose the corner back to the original size - local new_args = setmetatable({position = corner}, {__index=args}) - local ngeo = placement_private.align(d, new_args) - - return fix_new_geometry(ngeo, args, true), corner -end - ---- Place the client so no part of it will be outside the screen (workarea). --- --- ---![Usage example](../images/AUTOGEN_awful_placement_no_offscreen.svg) --- ---**Usage example output**: --- --- Before: x=-30, y=-30, width=100, height=100 --- After: x=10, y=10, width=100, height=100 --- --- --- @usage ---awful.placement.no_offscreen(c)--, {honor_workarea=true, margins=40}) --- @client c The client. --- @tparam[opt=client's screen] integer screen The screen. --- @treturn table The new client geometry. -function placement.no_offscreen(c, screen) - --HACK necessary for composition to work. The API will be changed soon - if type(screen) == "table" then - screen = nil - end - - c = c or capi.client.focus - 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 - - if geometry.x + geometry.width > screen_geometry.x + screen_geometry.width then - geometry.x = screen_geometry.x + screen_geometry.width - geometry.width - end - if geometry.x < screen_geometry.x then - geometry.x = screen_geometry.x - end - - if geometry.y + geometry.height > screen_geometry.y + screen_geometry.height then - geometry.y = screen_geometry.y + screen_geometry.height - geometry.height - end - if geometry.y < screen_geometry.y then - geometry.y = screen_geometry.y - end - - return c:geometry { - x = geometry.x, - y = geometry.y - } -end - ---- Place the client where there's place available with minimum overlap. --- --- ---![Usage example](../images/AUTOGEN_awful_placement_no_overlap.svg) --- --- @usage ---awful.placement.no_overlap(client.focus) ---local x,y = screen[4].geometry.x, screen[4].geometry.y --- @param c The client. --- @treturn table The new geometry -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) - local curlay = layout.get() - local areas = { screen.workarea } - for _, cl in pairs(cls) do - if cl ~= c and cl.type ~= "desktop" and (cl.floating or curlay == layout.suit.floating) then - areas = grect.area_remove(areas, area_common(cl)) - end - end - - -- Look for available space - local found = false - local new = { x = geometry.x, y = geometry.y, width = 0, height = 0 } - for _, r in ipairs(areas) do - if r.width >= geometry.width - and r.height >= geometry.height - and r.width * r.height > new.width * new.height then - found = true - new = r - -- Check if the client's current position is available - -- and prefer that one (why move it around pointlessly?) - if geometry.x >= r.x - and geometry.y >= r.y - and geometry.x + geometry.width <= r.x + r.width - and geometry.y + geometry.height <= r.y + r.height then - new.x = geometry.x - new.y = geometry.y - end - end - end - - -- We did not find an area with enough space for our size: - -- just take the biggest available one and go in. - -- This makes sure to have the whole screen's area in case it has been - -- removed. - if not found then - if #areas == 0 then - areas = { screen.workarea } - end - for _, r in ipairs(areas) do - if r.width * r.height > new.width * new.height then - new = r - end - end - end - - -- Restore height and width - new.width = geometry.width - new.height = geometry.height - - return c:geometry({ x = new.x, y = new.y }) -end - ---- Place the client under the mouse. --- --- ---![Usage example](../images/AUTOGEN_awful_placement_under_mouse.svg) --- --- @usage ---awful.placement.under_mouse(client.focus) --- @tparam drawable d A drawable (like `client`, `mouse` or `wibox`) --- @tparam[opt={}] table args Other arguments --- @treturn table The new geometry -function placement.under_mouse(d, args) - args = add_context(args, "under_mouse") - d = d or capi.client.focus - - local m_coords = capi.mouse.coords() - - local ngeo = geometry_common(d, args) - ngeo.x = math.floor(m_coords.x - ngeo.width / 2) - ngeo.y = math.floor(m_coords.y - ngeo.height / 2) - - local bw = d.border_width or 0 - ngeo.width = ngeo.width - 2*bw - ngeo.height = ngeo.height - 2*bw - - geometry_common(d, args, ngeo) - - return fix_new_geometry(ngeo, args, true) -end - ---- Place the client next to the mouse. --- --- It will place `c` next to the mouse pointer, trying the following positions --- in this order: right, left, above and below. --- --- ---![Usage example](../images/AUTOGEN_awful_placement_next_to_mouse.svg) --- --- @usage ---awful.placement.next_to_mouse(client.focus) --- @tparam drawable d A drawable (like `client`, `mouse` or `wibox`) --- @tparam[opt={}] table args Other arguments --- @treturn table The new geometry -function placement.next_to_mouse(d, args) - if type(args) == "number" then - util.deprecate( - "awful.placement.next_to_mouse offset argument is deprecated".. - " use awful.placement.next_to_mouse(c, {offset={x=...}})" - ) - args = nil - end - - local old_args = args or {} - - args = add_context(args, "next_to_mouse") - d = d or capi.client.focus - - local sgeo = get_parent_geometry(d, args) - - args.pretend = true - args.parent = capi.mouse - - local ngeo = placement.left(d, args) - - if ngeo.x + ngeo.width > sgeo.x+sgeo.width then - ngeo = placement.right(d, args) - else - -- It is _next_ to mouse, not under_mouse - ngeo.x = ngeo.x+1 - end - - args.pretend = old_args.pretend - - geometry_common(d, args, ngeo) - - attach(d, placement.next_to_mouse, old_args) - - return fix_new_geometry(ngeo, args, true) -end - ---- Resize the drawable to the cursor. --- --- Valid args: --- --- * *axis*: The axis (vertical or horizontal). If none is --- specified, then the drawable will be resized on both axis. --- --- --- ---![Usage example](../images/AUTOGEN_awful_placement_resize_to_mouse.svg) --- --- @tparam drawable d A drawable (like `client`, `mouse` or `wibox`) --- @tparam[opt={}] table args Other arguments --- @treturn table The new geometry -function placement.resize_to_mouse(d, args) - d = d or capi.client.focus - args = add_context(args, "resize_to_mouse") - - local coords = capi.mouse.coords() - local ngeo = geometry_common(d, args) - local h_only = args.axis == "horizontal" - local v_only = args.axis == "vertical" - - -- To support both growing and shrinking the drawable, it is necessary - -- to decide to use either "north or south" and "east or west" directions. - -- Otherwise, the result will always be 1x1 - local _, closest_corner = placement.closest_corner(capi.mouse, { - parent = d, - pretend = true, - include_sides = args.include_sides or false, - }) - - -- Given "include_sides" wasn't set, it will always return a name - -- with the 2 axis. If only one axis is needed, adjust the result - if h_only then - closest_corner = closest_corner:match("left") or closest_corner:match("right") - elseif v_only then - closest_corner = closest_corner:match("top") or closest_corner:match("bottom") - end - - -- Use p0 (mouse), p1 and p2 to create a rectangle - local pts = resize_to_point_map[closest_corner] - local p1 = pts.p1 and rect_to_point(ngeo, pts.p1[1], pts.p1[2]) or coords - local p2 = pts.p2 and rect_to_point(ngeo, pts.p2[1], pts.p2[2]) or coords - - -- Create top_left and bottom_right points, convert to rectangle - ngeo = rect_from_points( - pts.y_only and ngeo.x or math.min(p1.x, p2.x), - pts.x_only and ngeo.y or math.min(p1.y, p2.y), - pts.y_only and ngeo.x + ngeo.width or math.max(p2.x, p1.x), - pts.x_only and ngeo.y + ngeo.height or math.max(p2.y, p1.y) - ) - - local bw = d.border_width or 0 - - for _, a in ipairs {"width", "height"} do - ngeo[a] = ngeo[a] - 2*bw - end - - -- Now, correct the geometry by the given size_hints offset - if d.apply_size_hints then - local w, h = d:apply_size_hints( - ngeo.width, - ngeo.height - ) - local offset = align_map[pts.align](w, h, ngeo.width, ngeo.height) - ngeo.x = ngeo.x - offset.x - ngeo.y = ngeo.y - offset.y - end - - geometry_common(d, args, ngeo) - - return fix_new_geometry(ngeo, args, true) -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 --- --- --- ---![Usage example](../images/AUTOGEN_awful_placement_align.svg) --- --- @tparam drawable d A drawable (like `client`, `mouse` or `wibox`) --- @tparam[opt={}] table args Other arguments --- @treturn table The new geometry -function placement.align(d, args) - args = add_context(args, "align") - 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 - ) - - local ngeo = { - x = (pos.x and math.ceil(sgeo.x + pos.x) or dgeo.x) , - y = (pos.y and math.ceil(sgeo.y + pos.y) or dgeo.y) , - width = math.ceil(dgeo.width ) - 2*bw, - height = math.ceil(dgeo.height ) - 2*bw, - } - - geometry_common(d, args, ngeo) - - attach(d, placement[args.position], args) - - return fix_new_geometry(ngeo, args, true) -end - --- Add the alias functions -for k in pairs(align_map) do - placement[k] = function(d, args) - args = add_context(args, k) - args.position = k - return placement_private.align(d, args) - end - reverse_align_map[placement[k]] = k -end - --- Add the documentation for align alias - ---- --- Align a client to the top left of the parent area. --- ---![Usage example](../images/AUTOGEN_awful_placement_top_left.svg) --- @tparam drawable d A drawable (like `client`, `mouse` or `wibox`) --- @tparam[opt={}] table args Other arguments') --- @treturn table The new geometry --- @name top_left --- @class function --- --- @usage ---awful.placement.top_left(client.focus) - ---- --- Align a client to the top right of the parent area. --- ---![Usage example](../images/AUTOGEN_awful_placement_top_right.svg) --- @tparam drawable d A drawable (like `client`, `mouse` or `wibox`) --- @tparam[opt={}] table args Other arguments') --- @treturn table The new geometry --- @name top_right --- @class function --- --- @usage ---awful.placement.top_right(client.focus) - ---- --- Align a client to the bottom left of the parent area. --- ---![Usage example](../images/AUTOGEN_awful_placement_bottom_left.svg) --- @tparam drawable d A drawable (like `client`, `mouse` or `wibox`) --- @tparam[opt={}] table args Other arguments') --- @treturn table The new geometry --- @name bottom_left --- @class function --- --- @usage ---awful.placement.bottom_left(client.focus) - ---- --- Align a client to the bottom right of the parent area. --- ---![Usage example](../images/AUTOGEN_awful_placement_bottom_right.svg) --- @tparam drawable d A drawable (like `client`, `mouse` or `wibox`) --- @tparam[opt={}] table args Other arguments') --- @treturn table The new geometry --- @name bottom_right --- @class function --- --- @usage ---awful.placement.bottom_right(client.focus) - ---- --- Align a client to the left of the parent area. --- ---![Usage example](../images/AUTOGEN_awful_placement_left.svg) --- @tparam drawable d A drawable (like `client`, `mouse` or `wibox`) --- @tparam[opt={}] table args Other arguments') --- @treturn table The new geometry --- @name left --- @class function --- --- @usage ---awful.placement.left(client.focus) - ---- --- Align a client to the right of the parent area. --- ---![Usage example](../images/AUTOGEN_awful_placement_right.svg) --- @tparam drawable d A drawable (like `client`, `mouse` or `wibox`) --- @tparam[opt={}] table args Other arguments') --- @treturn table The new geometry --- @name right --- @class function --- --- @usage ---awful.placement.right(client.focus) - ---- --- Align a client to the top of the parent area. --- ---![Usage example](../images/AUTOGEN_awful_placement_top.svg) --- @tparam drawable d A drawable (like `client`, `mouse` or `wibox`) --- @tparam[opt={}] table args Other arguments') --- @treturn table The new geometry --- @name top --- @class function --- --- @usage ---awful.placement.top(client.focus) ---assert(c.x == screen[1].geometry.width/2-40/2-c.border_width) - ---- --- Align a client to the bottom of the parent area. --- ---![Usage example](../images/AUTOGEN_awful_placement_bottom.svg) --- @tparam drawable d A drawable (like `client`, `mouse` or `wibox`) --- @tparam[opt={}] table args Other arguments') --- @treturn table The new geometry --- @name bottom --- @class function --- --- @usage ---awful.placement.bottom(client.focus) - ---- --- Align a client to the center of the parent area. --- ---![Usage example](../images/AUTOGEN_awful_placement_centered.svg) --- @tparam drawable d A drawable (like `client`, `mouse` or `wibox`) --- @tparam[opt={}] table args Other arguments') --- @treturn table The new geometry --- @name centered --- @class function --- --- @usage ---awful.placement.centered(client.focus) - ---- --- Align a client to the vertical center of the parent area. --- ---![Usage example](../images/AUTOGEN_awful_placement_center_vertical.svg) --- @tparam drawable d A drawable (like `client`, `mouse` or `wibox`) --- @tparam[opt={}] table args Other arguments') --- @name center_vertical --- @class function --- --- @usage ---awful.placement.center_vertical(client.focus) - ---- --- Align a client to the horizontal center left of the parent area. --- ---![Usage example](../images/AUTOGEN_awful_placement_center_horizontal.svg) --- @tparam drawable d A drawable (like `client`, `mouse` or `wibox`) --- @tparam[opt={}] table args Other arguments') --- @treturn table The new geometry --- @name center_horizontal --- @class function --- --- @usage ---awful.placement.center_horizontal(client.focus) - ---- Stretch a drawable in a specific direction. --- Valid args: --- --- * **direction**: The stretch direction (*left*, *right*, *up*, *down*) or --- a table with multiple directions. --- --- --- ---![Usage example](../images/AUTOGEN_awful_placement_stretch.svg) --- --- @tparam[opt=client.focus] drawable d A drawable (like `client` or `wibox`) --- @tparam[opt={}] table args The arguments --- @treturn table The new geometry -function placement.stretch(d, args) - args = add_context(args, "stretch") - - 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 - args.direction = dir - placement_private.stretch(dir, 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 - ngeo.width = dgeo.width + (dgeo.x - ngeo.x) - elseif args.direction == "right" then - ngeo.width = sgeo.width - ngeo.x - 2*bw - elseif args.direction == "up" then - ngeo.y = sgeo.y - ngeo.height = dgeo.height + (dgeo.y - ngeo.y) - elseif args.direction == "down" then - ngeo.height = sgeo.height - dgeo.y - 2*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) - - return fix_new_geometry(ngeo, args, true) -end - --- Add the alias functions -for _,v in ipairs {"left", "right", "up", "down"} do - placement["stretch_"..v] = function(d, args) - args = add_context(args, "stretch_"..v) - args.direction = v - return placement_private.stretch(d, args) - end -end - ---- --- Stretch the drawable to the left of the parent area. --- ---![Usage example](../images/AUTOGEN_awful_placement_stretch_left.svg) --- @tparam drawable d A drawable (like `client` or `wibox`) --- @tparam[opt={}] table args Other arguments --- @treturn table The new geometry --- @name stretch_left --- @class function --- --- @usage ---placement.stretch_left(client.focus) - ---- --- Stretch the drawable to the right of the parent area. --- ---![Usage example](../images/AUTOGEN_awful_placement_stretch_right.svg) --- @tparam drawable d A drawable (like `client` or `wibox`) --- @tparam[opt={}] table args Other arguments --- @treturn table The new geometry --- @name stretch_right --- @class function --- --- @usage ---placement.stretch_right(client.focus) - ---- --- Stretch the drawable to the top of the parent area. --- ---![Usage example](../images/AUTOGEN_awful_placement_stretch_up.svg) --- @tparam drawable d A drawable (like `client` or `wibox`) --- @tparam[opt={}] table args Other arguments --- @treturn table The new geometry --- @name stretch_up --- @class function --- --- @usage ---placement.stretch_up(client.focus) - ---- --- Stretch the drawable to the bottom of the parent area. --- ---![Usage example](../images/AUTOGEN_awful_placement_stretch_down.svg) --- @tparam drawable d A drawable (like `client` or `wibox`) --- @tparam[opt={}] table args Other arguments --- @treturn table The new geometry --- @name stretch_down --- @class function --- --- @usage ---placement.stretch_down(client.focus) - ---- Maximize a drawable horizontally, vertically or both. --- Valid args: --- --- * *axis*:The axis (vertical or horizontal). If none is --- specified, then the drawable will be maximized on both axis. --- --- --- ---![Usage example](../images/AUTOGEN_awful_placement_maximize.svg) --- --- @tparam[opt=client.focus] drawable d A drawable (like `client` or `wibox`) --- @tparam[opt={}] table args The arguments --- @treturn table The new geometry -function placement.maximize(d, args) - args = add_context(args, "maximize") - 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 - ngeo.height = sgeo.height - 2*bw - end - - if (not args.axis) or args.axis :match "horizontal" then - ngeo.x = sgeo.x - ngeo.width = sgeo.width - 2*bw - end - - geometry_common(d, args, ngeo) - - attach(d, placement.maximize, args) - - return fix_new_geometry(ngeo, args, true) -end - --- Add the alias functions -for _, v in ipairs {"vertically", "horizontally"} do - placement["maximize_"..v] = function(d2, args) - args = add_context(args, "maximize_"..v) - args.axis = v - return placement_private.maximize(d2, args) - end -end - ---- --- Vetically maximize the drawable in the parent area. --- ---![Usage example](../images/AUTOGEN_awful_placement_maximize_vertically.svg) --- @tparam drawable d A drawable (like `client` or `wibox`) --- @tparam[opt={}] table args Other arguments') --- @name maximize_vertically --- @class function --- --- @usage ---placement.maximize_vertically(c) - ---- --- Horizontally maximize the drawable in the parent area. --- ---![Usage example](../images/AUTOGEN_awful_placement_maximize_horizontally.svg) --- @tparam drawable d A drawable (like `client` or `wibox`) --- @tparam[opt={}] table args Other arguments') --- @name maximize_horizontally --- @class function --- --- @usage ---placement.maximize_horizontally(c) - ---- Scale the drawable by either a relative or absolute percent. --- --- Valid args: --- --- **to_percent** : A number between 0 and 1. It represent a percent related to --- the parent geometry. --- **by_percent** : A number between 0 and 1. It represent a percent related to --- the current size. --- **direction**: Nothing or "left", "right", "up", "down". --- --- @tparam[opt=client.focus] drawable d A drawable (like `client` or `wibox`) --- @tparam[opt={}] table args The arguments --- @treturn table The new geometry -function placement.scale(d, args) - args = add_context(args, "scale_to_percent") - d = d or capi.client.focus - - local to_percent = args.to_percent - local by_percent = args.by_percent - - local percent = to_percent or by_percent - - local direction = args.direction - - local sgeo = get_parent_geometry(d, args) - local ngeo = geometry_common(d, args, nil) - - local old_area = {width = ngeo.width, height = ngeo.height} - - if (not direction) or direction == "left" or direction == "right" then - ngeo.width = (to_percent and sgeo or ngeo).width*percent - - if direction == "left" then - ngeo.x = ngeo.x - (ngeo.width - old_area.width) - end - end - - if (not direction) or direction == "up" or direction == "down" then - ngeo.height = (to_percent and sgeo or ngeo).height*percent - - if direction == "up" then - ngeo.y = ngeo.y - (ngeo.height - old_area.height) - end - end - - local bw = d.border_width or 0 - ngeo.width = ngeo.width - 2*bw - ngeo.height = ngeo.height - 2*bw - - geometry_common(d, args, ngeo) - - attach(d, placement.maximize, args) - - return fix_new_geometry(ngeo, args, true) -end - ---- Move a drawable to a relative position next to another one. --- --- The `args.preferred_positions` look like this: --- --- {"top", "right", "left", "bottom"} --- --- In that case, if there is room on the top of the geomtry, then it will have --- priority, followed by all the others, in order. --- --- @tparam drawable d A wibox or client --- @tparam table args --- @tparam string args.mode The mode --- @tparam string args.preferred_positions The preferred positions (in order) --- @tparam string args.geometry A geometry inside the other drawable --- @treturn table The new geometry --- @treturn string The choosen position --- @treturn string The choosen direction -function placement.next_to(d, args) - args = add_context(args, "next_to") - d = d or capi.client.focus - - local preferred_positions = {} - - if #(args.preferred_positions or {}) then - for k, v in ipairs(args.preferred_positions) do - preferred_positions[v] = k - end - end - - local dgeo = geometry_common(d, args) - local pref_idx, pref_name = 99, nil - local mode,wgeo = args.mode - - if args.geometry then - mode = "geometry" - wgeo = args.geometry - else - local pos = capi.mouse.current_widget_geometry - - if pos then - wgeo, mode = pos, "cursor" - elseif capi.mouse.current_client then - wgeo, mode = capi.mouse.current_client:geometry(), "cursor" - end - end - - if not wgeo then return end - - -- See get_relative_regions comments - local is_absolute = wgeo.ontop ~= nil - - local regions = get_relative_regions(wgeo, mode, is_absolute) - - -- Check each possible slot around the drawable (8 total), see what fits - -- and order them by preferred_positions - local does_fit = {} - for k,v in pairs(regions) do - local geo, dir = outer_positions[k.."1"](v, dgeo.width, dgeo.height) - geo.width, geo.height = dgeo.width, dgeo.height - local fit = fit_in_bounding(v.screen, geo, args) - - -- Try the other compatible geometry - if not fit then - geo, dir = outer_positions[k.."2"](v, dgeo.width, dgeo.height) - geo.width, geo.height = dgeo.width, dgeo.height - fit = fit_in_bounding(v.screen, geo, args) - end - - does_fit[k] = fit and {geo, dir} or nil - - if fit and preferred_positions[k] and preferred_positions[k] < pref_idx then - pref_idx = preferred_positions[k] - pref_name = k - end - - -- No need to continue - if fit and preferred_positions[k] == 1 then break end - end - - local pos_name = pref_name or next(does_fit) - local ngeo, dir = unpack(does_fit[pos_name] or {}) --FIXME why does this happen - - geometry_common(d, args, ngeo) - - attach(d, placement.next_to, args) - - return fix_new_geometry(ngeo, args, true), pos_name, dir -end - ---- Restore the geometry. --- @tparam[opt=client.focus] drawable d A drawable (like `client` or `wibox`) --- @tparam[opt={}] table args The arguments --- @treturn boolean If the geometry was restored -function placement.restore(d, args) - if not args or not args.context then return false end - d = d or capi.client.focus - - if not data[d] then return false end - - local memento = data[d][args.context] - - if not memento then return false end - - memento.screen = nil --TODO use it - - d.border_width = memento.border_width - - d:geometry(memento) - return true -end - -return placement - --- vim: filetype=lua:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:textwidth=80 |