From df6bc2f6bc83986fbf7ee4049c50f59124954bdc Mon Sep 17 00:00:00 2001 From: Emmanuel Lepage Vallee Date: Sun, 15 May 2016 01:10:11 -0400 Subject: [PATCH 01/30] wibox: Make the table argument optional For consistency with other objects. --- lib/wibox/init.lua | 1 + 1 file changed, 1 insertion(+) diff --git a/lib/wibox/init.lua b/lib/wibox/init.lua index ae4693d9..28a191f8 100644 --- a/lib/wibox/init.lua +++ b/lib/wibox/init.lua @@ -297,6 +297,7 @@ local function setup_signals(_wibox) end local function new(args) + args = args or {} local ret = object() local w = capi.drawin(args) From 25f4f24791025e6364ee34bbf6a1b830199b497f Mon Sep 17 00:00:00 2001 From: Emmanuel Lepage Vallee Date: Sun, 8 May 2016 00:20:51 -0400 Subject: [PATCH 02/30] layoutbox: Fix a leak with Lua 5.1 and luajit Fixes #808 --- lib/awful/widget/layoutbox.lua | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/lib/awful/widget/layoutbox.lua b/lib/awful/widget/layoutbox.lua index ce16de10..cd5e0440 100644 --- a/lib/awful/widget/layoutbox.lua +++ b/lib/awful/widget/layoutbox.lua @@ -8,10 +8,9 @@ --------------------------------------------------------------------------- local setmetatable = setmetatable -local capi = { screen = screen } +local capi = { screen = screen, tag = tag } local layout = require("awful.layout") local tooltip = require("awful.tooltip") -local tag = require("awful.tag") local beautiful = require("beautiful") local imagebox = require("wibox.widget.imagebox") @@ -48,8 +47,8 @@ function layoutbox.new(screen) -- Do we already have the update callbacks registered? if boxes == nil then boxes = setmetatable({}, { __mode = "kv" }) - tag.attached_connect_signal(nil, "property::selected", update_from_tag) - tag.attached_connect_signal(nil, "property::layout", update_from_tag) + capi.tag.connect_signal("property::selected", update_from_tag) + capi.tag.connect_signal("property::layout", update_from_tag) layoutbox.boxes = boxes end @@ -60,7 +59,7 @@ function layoutbox.new(screen) w._layoutbox_tooltip = tooltip({ objects = {w}, delay_show = 1 }) update(w, screen) - boxes[screen] = w + boxes[screen] = setmetatable({}, {__index = w, __newindex = w}) end return w From 992b03d8d2f1cc8f85ada81bcaeface2eb0b7109 Mon Sep 17 00:00:00 2001 From: Uli Schlachter Date: Sat, 14 May 2016 17:34:06 -0400 Subject: [PATCH 03/30] struts: Avoid an integer underflow Fixes #900 --- objects/screen.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/objects/screen.c b/objects/screen.c index 9081be8c..42207cf7 100644 --- a/objects/screen.c +++ b/objects/screen.c @@ -756,8 +756,8 @@ void screen_update_workarea(screen_t *screen) area.x += left; area.y += top; - area.width -= left + right; - area.height -= top + bottom; + area.width -= MIN(area.width, left + right); + area.height -= MIN(area.height, top + bottom); if (AREA_EQUAL(area, screen->workarea)) return; From f4b2043da54aac62a9e62f00f045145dd725d765 Mon Sep 17 00:00:00 2001 From: Emmanuel Lepage Vallee Date: Mon, 9 May 2016 23:56:29 -0400 Subject: [PATCH 04/30] tests: Fix the wibox template --- tests/examples/wibox/template.lua | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/tests/examples/wibox/template.lua b/tests/examples/wibox/template.lua index c035185a..570c0dee 100644 --- a/tests/examples/wibox/template.lua +++ b/tests/examples/wibox/template.lua @@ -1,11 +1,12 @@ local file_path, image_path, luacovpath = ... -- Set the global shims --- luacheck: globals awesome client tag drawin +-- luacheck: globals awesome client tag drawin screen awesome = require( "awesome" ) client = require( "client" ) tag = require( "tag" ) drawin = require( "drawin" ) +screen = require( "screen" ) -- Force luacheck to be silent about setting those as unused globals assert(awesome and client and tag) From 43ef623dc65085021ed38289dfc92181e9d4053f Mon Sep 17 00:00:00 2001 From: Emmanuel Lepage Vallee Date: Tue, 10 May 2016 00:01:12 -0400 Subject: [PATCH 05/30] client: Add x, y, width and height properties --- lib/awful/client.lua | 48 ++++++++++++++++++++++++++++++++++++++++++++ objects/client.c | 9 --------- 2 files changed, 48 insertions(+), 9 deletions(-) diff --git a/lib/awful/client.lua b/lib/awful/client.lua index fdad1605..bbc008a7 100644 --- a/lib/awful/client.lua +++ b/lib/awful/client.lua @@ -693,6 +693,54 @@ function client.floating.delete(c) client.object.set_floating(c, nil) end +--- The x coordinates. +-- +-- **Signal:** +-- +-- * *property::x* +-- +-- @property x +-- @param integer + +--- The y coordinates. +-- +-- **Signal:** +-- +-- * *property::y* +-- +-- @property y +-- @param integer + +--- The width of the wibox. +-- +-- **Signal:** +-- +-- * *property::width* +-- +-- @property width +-- @param width + +--- The height of the wibox. +-- +-- **Signal:** +-- +-- * *property::height* +-- +-- @property height +-- @param height + +-- Add the geometry helpers to match the wibox API +for _, v in ipairs {"x", "y", "width", "height"} do + client.object["get_"..v] = function(c) + return c:geometry()[v] + end + + client.object["set_"..v] = function(c, value) + return c:geometry({[v] = value}) + end +end + + --- Restore (=unminimize) a random client. -- @function awful.client.restore -- @param s The screen to use. diff --git a/objects/client.c b/objects/client.c index dababb9f..c696f06f 100644 --- a/objects/client.c +++ b/objects/client.c @@ -3423,21 +3423,12 @@ client_class_setup(lua_State *L) signal_add(&client_class.signals, "property::transient_for"); signal_add(&client_class.signals, "property::type"); signal_add(&client_class.signals, "property::urgent"); - /** - * @signal property::width - */ signal_add(&client_class.signals, "property::width"); /** * @signal property::window */ signal_add(&client_class.signals, "property::window"); - /** - * @signal property::x - */ signal_add(&client_class.signals, "property::x"); - /** - * @signal property::y - */ signal_add(&client_class.signals, "property::y"); /** When a client should get activated (focused and/or raised). * From 9a72062cacbaadb9c34ad54e2804429ed058774a Mon Sep 17 00:00:00 2001 From: Emmanuel Lepage Vallee Date: Sat, 7 May 2016 23:46:57 -0400 Subject: [PATCH 06/30] wibox: Turn into "real" objects. Before this commit, it was necessary to call 'rawset' to be able to add new fields to the wibox. This is no longer required. This solution was choosen because wibox is itself a base class of menus and wibars. Those classes can now add new properties without hacks. --- lib/wibox/init.lua | 17 +++++++++++++++-- 1 file changed, 15 insertions(+), 2 deletions(-) diff --git a/lib/wibox/init.lua b/lib/wibox/init.lua index 28a191f8..f594acd0 100644 --- a/lib/wibox/init.lua +++ b/lib/wibox/init.lua @@ -26,6 +26,11 @@ wibox.widget = require("wibox.widget") wibox.drawable = require("wibox.drawable") wibox.hierarchy = require("wibox.hierarchy") +local force_forward = { + shape_bounding = true, + shape_clip = true, +} + --- Set the widget that the wibox displays function wibox:set_widget(widget) self._drawable:set_widget(widget) @@ -340,10 +345,18 @@ local function new(args) -- Make sure the wibox is drawn at least once ret.draw() - -- Redirect all non-existing indexes to the "real" drawin + -- If a value is not found, look in the drawin setmetatable(ret, { __index = w, - __newindex = w + __newindex = function(self, k,v) + if wibox["set_"..k] then + wibox["set_"..k](v) + elseif w[k] ~= nil or force_forward[k] then + w[k] = v + else + rawset(self, k, v) + end + end }) return ret From 129b3b1d50fe38c60d2e008a386fbd4813d0e616 Mon Sep 17 00:00:00 2001 From: Emmanuel Lepage Vallee Date: Sun, 8 May 2016 01:41:19 -0400 Subject: [PATCH 07/30] wibox: Add wibox property support Just like the miss handler system, but for wiboxes. This will simplify the API as there was a mix of properties and accessors in the API doc. --- lib/wibox/init.lua | 31 +++++++++++++++++++++++-------- 1 file changed, 23 insertions(+), 8 deletions(-) diff --git a/lib/wibox/init.lua b/lib/wibox/init.lua index f594acd0..b0fa7c08 100644 --- a/lib/wibox/init.lua +++ b/lib/wibox/init.lua @@ -213,11 +213,11 @@ end --- The default background color. -- @beautiful beautiful.bg_normal --- @see set_bg +-- @see bg --- The default foreground (text) color. -- @beautiful beautiful.fg_normal --- @see set_fg +-- @see fg --- Set a declarative widget hierarchy description. -- See [The declarative layout system](../documentation/03-declarative-layout.md.html) @@ -226,24 +226,33 @@ end -- @class function wibox.setup = base.widget.setup ---- Set the background of the wibox +--- The background of the wibox. -- @param c The background to use. This must either be a cairo pattern object, -- nil or a string that gears.color() understands. +-- @property bg +-- @see gears.color + function wibox:set_bg(c) self._drawable:set_bg(c) end ---- Set the background image of the drawable +--- The background image of the drawable. -- If `image` is a function, it will be called with `(context, cr, width, height)` -- as arguments. Any other arguments passed to this method will be appended. -- @param image A background image or a function +-- @property bgimage +-- @see gears.surface + function wibox:set_bgimage(image, ...) self._drawable:set_bgimage(image, ...) end ---- Set the foreground of the wibox +--- The foreground (text) of the wibox. -- @param c The foreground to use. This must either be a cairo pattern object, -- nil or a string that gears.color() understands. +-- @property fg +-- @see gears.color + function wibox:set_fg(c) self._drawable:set_fg(c) end @@ -347,10 +356,16 @@ local function new(args) -- If a value is not found, look in the drawin setmetatable(ret, { - __index = w, + __index = function(self, k) + if rawget(self, "get_"..k) then + return self["get_"..k](self) + else + return w[k] + end + end, __newindex = function(self, k,v) - if wibox["set_"..k] then - wibox["set_"..k](v) + if rawget(self, "set_"..k) then + self["set_"..k](self, v) elseif w[k] ~= nil or force_forward[k] then w[k] = v else From 0dcc545f35a6170b534113448a0747b142f7a580 Mon Sep 17 00:00:00 2001 From: Emmanuel Lepage Vallee Date: Thu, 12 May 2016 01:18:12 -0400 Subject: [PATCH 08/30] tests: Update the miss handler test --- tests/test-miss-handlers.lua | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/tests/test-miss-handlers.lua b/tests/test-miss-handlers.lua index fe61a860..258cbb99 100644 --- a/tests/test-miss-handlers.lua +++ b/tests/test-miss-handlers.lua @@ -4,6 +4,7 @@ local mouse = mouse local class = tag local obj = class({}) local handler = require("gears.object.properties") +local wibox = require("wibox") awesome.connect_signal("debug::index::miss", error) awesome.connect_signal("debug::newindex::miss", error) @@ -35,4 +36,8 @@ assert(obj.key == 1337) mouse.foo = "bar" assert(mouse.foo == "bar") +local w = wibox() +w.foo = "bar" +assert(w.foo == "bar") + require("_runner").run_steps({ function() return true end }) From a141dbfd063bd5ac71191f4a356a4e03d0683249 Mon Sep 17 00:00:00 2001 From: Emmanuel Lepage Vallee Date: Tue, 10 May 2016 00:01:35 -0400 Subject: [PATCH 09/30] wibox: Add screen properties --- lib/wibox/init.lua | 16 +++++++++++++++- 1 file changed, 15 insertions(+), 1 deletion(-) diff --git a/lib/wibox/init.lua b/lib/wibox/init.lua index b0fa7c08..0a6bed50 100644 --- a/lib/wibox/init.lua +++ b/lib/wibox/init.lua @@ -8,7 +8,8 @@ local capi = { drawin = drawin, root = root, - awesome = awesome + awesome = awesome, + screen = screen } local setmetatable = setmetatable local pairs = pairs @@ -16,6 +17,7 @@ local type = type local object = require("gears.object") local beautiful = require("beautiful") local base = require("wibox.widget.base") +local ascreen = require("awful.screen") --- This provides widget box windows. Every wibox can also be used as if it were -- a drawin. All drawin functions and properties are also available on wiboxes! @@ -268,6 +270,18 @@ function wibox:find_widgets(x, y) return self._drawable:find_widgets(x, y) end +function wibox:get_screen() + return capi.screen[ascreen.getbycoord(self.x, self.y)] +end + +function wibox:set_screen(s) + s = capi.screen[s or 1] + if s ~= self:get_screen() then + self.x = s.geometry.x + self.y = s.geometry.y + end +end + for _, k in pairs{ "buttons", "struts", "geometry", "get_xproperty", "set_xproperty" } do wibox[k] = function(self, ...) return self.drawin[k](self.drawin, ...) From c490ca380340578dc4a7bd0992c05062927fef17 Mon Sep 17 00:00:00 2001 From: Emmanuel Lepage Vallee Date: Tue, 10 May 2016 17:37:38 -0400 Subject: [PATCH 10/30] placement: Fix margin support This was only partially implemented. The margins were substracted from the area too early in the pipeline. Now, they are added when getting the size and substracted when setting it. This way, the margins will "survive" when a placement function set an absolute value in one of the field. Previously, this caused one (or more) of the margins to be lost. --- lib/awful/placement.lua | 82 +++++++++++++++++++++++++---------------- 1 file changed, 51 insertions(+), 31 deletions(-) diff --git a/lib/awful/placement.lua b/lib/awful/placement.lua index 79392316..b7b55b7b 100644 --- a/lib/awful/placement.lua +++ b/lib/awful/placement.lua @@ -137,6 +137,7 @@ local function compose(...) local pretend_real = args.pretend args.pretend = true + args.attach = false for k, f in ipairs(queue) do if k == #queue then @@ -259,29 +260,51 @@ local function store_geometry(d, reqtype) data[d][reqtype].screen = d.screen 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) - if args.pretend or not new_geo then return nil end +local function fix_new_geometry(new_geo, args, force) + if (args.pretend and not force) or not new_geo then return nil end - local offset = args.offset or {} - - if type(offset) == "number" then - offset = { - x = offset, - y = offset, - width = offset, - height = offset, - } - end + local m, offset = get_decoration(args) return { - x = new_geo.x and (new_geo.x + (offset.x or 0)), - y = new_geo.y and (new_geo.y + (offset.y or 0)), - width = new_geo.width and (new_geo.width + (offset.width or 0)), - height = new_geo.height and (new_geo.height + (offset.height or 0)), + x = new_geo.x and (new_geo.x + (offset.x or 0) + (m.left or 0) ), + y = new_geo.y and (new_geo.y + (offset.y or 0) + (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 @@ -338,16 +361,13 @@ local function geometry_common(obj, args, 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 - } + 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), + 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 @@ -633,7 +653,7 @@ function placement.closest_corner(d, args) local new_args = setmetatable({position = corner}, {__index=args}) local ngeo = placement_private.align(d, new_args) - return ngeo, corner + return fix_new_geometry(ngeo, args, true), corner end --- Place the client so no part of it will be outside the screen (workarea). @@ -751,7 +771,7 @@ function placement.under_mouse(d, args) ngeo.width = ngeo.width - 2*bw ngeo.height = ngeo.height - 2*bw - return ngeo + return fix_new_geometry(ngeo, args, true) end --- Place the client next to the mouse. @@ -862,7 +882,7 @@ function placement.resize_to_mouse(d, args) geometry_common(d, args, ngeo) - return ngeo + return fix_new_geometry(ngeo, args, true) end --- Move the drawable (client or wibox) `d` to a screen position or side. @@ -913,7 +933,7 @@ function placement.align(d, args) attach(d, placement[args.position], args) - return ngeo + return fix_new_geometry(ngeo, args, true) end -- Add the alias functions @@ -1002,7 +1022,7 @@ function placement.stretch(d, args) attach(d, placement["stretch_"..args.direction], args) - return ngeo + return fix_new_geometry(ngeo, args, true) end -- Add the alias functions @@ -1056,7 +1076,7 @@ function placement.maximize(d, args) attach(d, placement.maximize, args) - return ngeo + return fix_new_geometry(ngeo, args, true) end -- Add the alias functions @@ -1121,7 +1141,7 @@ function placement.scale(d, args) attach(d, placement.maximize, args) - return ngeo + return fix_new_geometry(ngeo, args, true) end ---@DOC_awful_placement_maximize_vertically_EXAMPLE@ From e78a07574b5201017370e67d449b64337b2b09ff Mon Sep 17 00:00:00 2001 From: Emmanuel Lepage Vallee Date: Sat, 7 May 2016 00:18:39 -0400 Subject: [PATCH 11/30] placement: Do not forward the args in get_parent_geometry The args are for 'd', not the parent. --- lib/awful/placement.lua | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/lib/awful/placement.lua b/lib/awful/placement.lua index b7b55b7b..5033f682 100644 --- a/lib/awful/placement.lua +++ b/lib/awful/placement.lua @@ -387,12 +387,18 @@ end -- @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, args) + return geometry_common(args.parent, {}) elseif obj.screen then - return geometry_common(obj.screen, args) + return geometry_common(obj.screen, { + honor_padding = args.honor_padding, + honor_workarea = args.honor_workarea + }) else return geometry_common(capi.screen[capi.mouse.screen], args) end From 45ff7efce5200082369ddf397115add2d4aa7e9d Mon Sep 17 00:00:00 2001 From: Emmanuel Lepage Vallee Date: Sat, 7 May 2016 00:52:39 -0400 Subject: [PATCH 12/30] placement: Support 'attach' in composited placement functions --- lib/awful/placement.lua | 71 ++++++++++++++++++++++++++++------------- 1 file changed, 48 insertions(+), 23 deletions(-) diff --git a/lib/awful/placement.lua b/lib/awful/placement.lua index 5033f682..8ea8fba1 100644 --- a/lib/awful/placement.lua +++ b/lib/awful/placement.lua @@ -96,6 +96,15 @@ local function get_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 @@ -123,7 +132,8 @@ local function compose(...) end end - local ret = wrap_client(function(d, args, ...) + local ret + ret = wrap_client(function(d, args, ...) local rets = {} local last_geo = nil @@ -135,6 +145,7 @@ local function compose(...) -- Force the "pretend" argument and restore the original value for -- the last node. local pretend_real = args.pretend + local attach_real = args.attach args.pretend = true args.attach = false @@ -161,6 +172,11 @@ local function compose(...) end end + if attach_real then + args.attach = true + attach(d, ret, args) + end + return last_geo, rets end, "compose") @@ -190,7 +206,7 @@ local placement_private = {} -- -- (awful.placement.no_overlap + awful.placement.no_offscreen)(c) -- -local placement = setmetatable({}, { +placement = setmetatable({}, { __index = placement_private, __newindex = function(_, k, f) placement_private[k] = wrap_client(f, k) @@ -222,9 +238,6 @@ local align_map = { center_horizontal = function(sw, _ , dw, _ ) return {x=sw/2-dw/2, y= nil } end, } --- Store function -> keys -local reverse_align_map = {} - -- Some parameters to correctly compute the final size local resize_to_point_map = { -- Corners @@ -308,13 +321,13 @@ local function fix_new_geometry(new_geo, args, force) } end ---- Get the area covered by a drawin. +-- 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. -local function area_common(d, new_geo, ignore_border_width, args) +area_common = function(d, new_geo, ignore_border_width, args) -- The C side expect no arguments, nil isn't valid 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 @@ -339,7 +352,6 @@ end -- @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) @@ -431,7 +443,7 @@ local function move_into_geometry(source, target) end -- Update the workarea -local function wibox_update_strut(d, position) +wibox_update_strut = function(d, position, args) -- If the drawable isn't visible, remove the struts if not d.visible then d:struts { left = 0, right = 0, bottom = 0, top = 0 } @@ -446,16 +458,18 @@ local function wibox_update_strut(d, position) -- 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 + 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 + struts[v] = geo.height + m[v] end end end @@ -464,16 +478,18 @@ local function wibox_update_strut(d, position) d:struts(struts) end ---- Pin a drawable to a placement function. +-- 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) +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 @@ -492,27 +508,36 @@ local function attach(d, position_f, args) position_f(d, args) end - d:connect_signal("property::width" , tracker) - d:connect_signal("property::height", tracker) + d:connect_signal("property::width" , tracker) + d:connect_signal("property::height" , tracker) + d:connect_signal("property::border_width", tracker) - 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 - 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) + 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 or mouse, also track it - local parent = args.parent or d.screen if parent then - args.parent:connect_signal("property::geometry" , tracker) + parent:connect_signal("property::geometry" , tracker) end end From 21c9766aa605c79c5a4c1cd8e188e2f05e989724 Mon Sep 17 00:00:00 2001 From: Emmanuel Lepage Vallee Date: Sun, 15 May 2016 03:26:58 -0400 Subject: [PATCH 13/30] placement: Fix under_mouse The function stopped actually setting the geometry... This was missed by tests because of an oversaw elsewhere. --- lib/awful/mouse/init.lua | 19 ++++--------------- lib/awful/placement.lua | 15 +++++++++------ 2 files changed, 13 insertions(+), 21 deletions(-) diff --git a/lib/awful/mouse/init.lua b/lib/awful/mouse/init.lua index 23d11d4b..9e094fb6 100644 --- a/lib/awful/mouse/init.lua +++ b/lib/awful/mouse/init.lua @@ -234,22 +234,11 @@ function mouse.resize_handler(c, context, hints) local lay = c.screen.selected_tag.layout if lay == layout.suit.floating or c.floating then - local offset = hints and hints.offset or {} - - if type(offset) == "number" then - offset = { - x = offset, - y = offset, - width = offset, - height = offset, - } - end - c:geometry { - x = hints.x + (offset.x or 0 ), - y = hints.y + (offset.y or 0 ), - width = hints.width + (offset.width or 0 ), - height = hints.height + (offset.height or 0 ), + x = hints.x, + y = hints.y, + width = hints.width, + height = hints.height, } elseif lay.resize_handler then lay.resize_handler(c, context, hints) diff --git a/lib/awful/placement.lua b/lib/awful/placement.lua index 8ea8fba1..f79fca4c 100644 --- a/lib/awful/placement.lua +++ b/lib/awful/placement.lua @@ -144,15 +144,16 @@ local function compose(...) -- 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 pretend_real = args.pretend - local attach_real = args.attach - - args.pretend = true - args.attach = false + local attach_real = args.attach + args.pretend = true + args.attach = false + args.offset = {} for k, f in ipairs(queue) do if k == #queue then - args.pretend = pretend_real or false + -- Let them fallback to the parent table + args.pretend = nil + args.offset = nil end local r = {f(d, args, ...)} @@ -802,6 +803,8 @@ function placement.under_mouse(d, args) 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 From b4f08eb7b6f3b79e1c7f74a3275fcbfbe73c60d4 Mon Sep 17 00:00:00 2001 From: Emmanuel Lepage Vallee Date: Sun, 15 May 2016 03:31:17 -0400 Subject: [PATCH 14/30] tests: Add asserts to under_mouse --- tests/examples/awful/placement/under_mouse.lua | 3 +++ 1 file changed, 3 insertions(+) diff --git a/tests/examples/awful/placement/under_mouse.lua b/tests/examples/awful/placement/under_mouse.lua index e1b40765..adcb2b80 100644 --- a/tests/examples/awful/placement/under_mouse.lua +++ b/tests/examples/awful/placement/under_mouse.lua @@ -6,3 +6,6 @@ mouse.coords {x= 50, y=50} --DOC_HIDE mouse.push_history() --DOC_HIDE awful.placement.under_mouse(client.focus) + +assert(client.focus.x + client.focus.width /2 - mouse.coords().x <= 1) --DOC_HIDE +assert(client.focus.y + client.focus.height/2 - mouse.coords().y <= 1) --DOC_HIDE From 50c97ed167fa8cd87694c5a90235659d0425f452 Mon Sep 17 00:00:00 2001 From: Emmanuel Lepage Vallee Date: Sat, 7 May 2016 01:45:50 -0400 Subject: [PATCH 15/30] placement: Add a callback to detach the placement function --- lib/awful/placement.lua | 42 +++++++++++++++++++++++++++++++++++++++++ 1 file changed, 42 insertions(+) diff --git a/lib/awful/placement.lua b/lib/awful/placement.lua index f79fca4c..be462a16 100644 --- a/lib/awful/placement.lua +++ b/lib/awful/placement.lua @@ -51,6 +51,11 @@ -- -- **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. @@ -505,6 +510,16 @@ attach = function(d, position_f, args) 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 @@ -540,6 +555,33 @@ attach = function(d, position_f, args) if parent then parent:connect_signal("property::geometry" , tracker) end + + -- Create a way to detach a placement function + d:add_signal("property::detach_callback") + 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 --- Check if an area intersect another area. From 833ad952f668ef38f8ba27c244594bfde5555c4d Mon Sep 17 00:00:00 2001 From: Emmanuel Lepage Vallee Date: Sat, 7 May 2016 02:55:23 -0400 Subject: [PATCH 16/30] awful.wibox: Port to the placement API --- lib/awful/wibox.lua | 391 ++++++++++++++++++++++++-------------------- 1 file changed, 212 insertions(+), 179 deletions(-) diff --git a/lib/awful/wibox.lua b/lib/awful/wibox.lua index 9cc1e18a..fef12b33 100644 --- a/lib/awful/wibox.lua +++ b/lib/awful/wibox.lua @@ -3,8 +3,8 @@ -- This module allows you to easily create wibox and attach them to the edge of -- a screen. -- --- @author Julien Danjou <julien@danjou.info> --- @copyright 2009 Julien Danjou +-- @author Emmanuel Lepage Vallee <elv1313@gmail.com> +-- @copyright 2016 Emmanuel Lepage Vallee -- @release @AWESOME_VERSION@ -- @module awful.wibox --------------------------------------------------------------------------- @@ -18,11 +18,11 @@ local capi = local setmetatable = setmetatable local tostring = tostring local ipairs = ipairs -local table = table local error = error local wibox = require("wibox") local beautiful = require("beautiful") -local round = require("awful.util").round +local util = require("awful.util") +local placement = require("awful.placement") local function get_screen(s) return s and capi.screen[s] @@ -32,77 +32,157 @@ local awfulwibox = { mt = {} } --- Array of table with wiboxes inside. -- It's an array so it is ordered. -local wiboxes = {} +local wiboxes = setmetatable({}, {__mode = "v"}) + +-- Compute the margin on one side +local function get_margin(w, position, auto_stop) + local h_or_w = (position == "top" or position == "bottom") and "height" or "width" + local ret = 0 + + for _, v in ipairs(wiboxes) do + -- Ignore the wiboxes placed after this one + if auto_stop and v == w then break end + + if v.position == position and v.screen == w.screen and v.visible then + ret = ret + v[h_or_w] + end + end + + return ret +end + +-- `honor_workarea` cannot be used as it does modify the workarea itself. +-- a manual padding has to be generated. +local function get_margins(w) + local position = w.position + assert(position) + + local margins = {left=0, right=0, top=0, bottom=0} + + margins[position] = get_margin(w, position, true) + + -- Avoid overlapping wiboxes + if position == "left" or position == "right" then + margins.top = get_margin(w, "top" ) + margins.bottom = get_margin(w, "bottom") + end + + return margins +end + +-- Create the placement function +local function gen_placement(position, stretch) + local maximize = (position == "right" or position == "left") and + "maximize_vertically" or "maximize_horizontally" + + return placement[position] + (stretch and placement[maximize] or nil) +end + +-- Attach the placement function. +local function attach(wb, align) + gen_placement(align, wb._stretch)(wb, { + attach = true, + update_workarea = true, + margins = get_margins(wb) + }) +end + +-- Re-attach all wiboxes on a given wibox screen +local function reattach(wb) + local s = wb.screen + for k, w in ipairs(wiboxes) do + if w ~= wb and w.screen == s then + if w.detach_callback then + w.detach_callback() + w.detach_callback = nil + end + attach(w, w.position) + end + end +end + +--- The wibox position. +-- @property position +-- @param string Either "left", right", "top" or "bottom" + +local function get_position(wb) + return wb._position or "top" +end + +local function set_position(wb, position) + -- Detach first to avoid any uneeded callbacks + if wb.detach_callback then + wb.detach_callback() + + -- Avoid disconnecting twice, this produces a lot of warnings + wb.detach_callback = nil + end + + -- Move the wibox to the end of the list to avoid messing up the others in + -- case there is stacked wiboxes on one side. + if wb._position then + for k, w in ipairs(wiboxes) do + if w == wb then + table.remove(wiboxes, k) + end + end + table.insert(wiboxes, wb) + end + + -- In case the position changed, it may be necessary to reset the size + if (wb._position == "left" or wb._position == "right") + and (position == "top" or position == "bottom") then + wb.height = math.ceil(beautiful.get_font_height(wb.font) * 1.5) + elseif (wb._position == "top" or wb._position == "bottom") + and (position == "left" or position == "right") then + wb.width = math.ceil(beautiful.get_font_height(wb.font) * 1.5) + end + + -- Changing the position will also cause the other margins to be invalidated. + -- For example, adding a wibox to the top will change the margins of any left + -- or right wiboxes. To solve, this, they need to be re-attached. + reattach(wb) + + -- Set the new position + wb._position = position + + -- Attach to the new position + attach(wb, position) +end + +--- Stretch the wibox. +-- +-- @property stretch +-- @param[opt=true] boolean + +local function get_stretch(w) + return w._stretch +end + +local function set_stretch(w, value) + w._stretch = value + + attach(w, w.position) +end --- Get a wibox position if it has been set, or return top. -- @param wb The wibox +-- @deprecated awful.wibox.get_position -- @return The wibox position. function awfulwibox.get_position(wb) - for _, wprop in ipairs(wiboxes) do - if wprop.wibox == wb then - return wprop.position - end - end - return "top" + util.deprecate("Use wb:get_position() instead of awful.wibox.get_position") + return get_position(wb) end --- Put a wibox on a screen at this position. -- @param wb The wibox to attach. -- @param position The position: top, bottom left or right. --- @param screen If the wibox it not attached to a screen, specified on which --- screen the position should be set. -function awfulwibox.set_position(wb, position, screen) - local area = get_screen(screen).geometry +-- @param screen This argument is deprecated, use wb.screen directly. +-- @deprecated awful.wibox.set_position +function awfulwibox.set_position(wb, position, screen) --luacheck: no unused args + util.deprecate("Use wb:set_position(position) instead of awful.wibox.set_position") - -- The "length" of a wibox is always chosen to be the optimal size - -- (non-floating). - -- The "width" of a wibox is kept if it exists. - if position == "right" then - wb.x = area.x + area.width - (wb.width + 2 * wb.border_width) - elseif position == "left" then - wb.x = area.x - elseif position == "bottom" then - wb.y = (area.y + area.height) - (wb.height + 2 * wb.border_width) - elseif position == "top" then - wb.y = area.y - end - - for _, wprop in ipairs(wiboxes) do - if wprop.wibox == wb then - wprop.position = position - break - end - end -end - ---- Reset all wiboxes positions. -local function update_all_wiboxes_position() - for _, wprop in ipairs(wiboxes) do - awfulwibox.set_position(wprop.wibox, wprop.position, wprop.screen) - end -end - -local function call_wibox_position_hook_on_prop_update() - update_all_wiboxes_position() -end - -local function wibox_update_strut(wb) - for _, wprop in ipairs(wiboxes) do - if wprop.wibox == wb then - if not wb.visible then - wb:struts { left = 0, right = 0, bottom = 0, top = 0 } - elseif wprop.position == "top" then - wb:struts { left = 0, right = 0, bottom = 0, top = wb.height + 2 * wb.border_width } - elseif wprop.position == "bottom" then - wb:struts { left = 0, right = 0, bottom = wb.height + 2 * wb.border_width, top = 0 } - elseif wprop.position == "left" then - wb:struts { left = wb.width + 2 * wb.border_width, right = 0, bottom = 0, top = 0 } - elseif wprop.position == "right" then - wb:struts { left = 0, right = wb.width + 2 * wb.border_width, bottom = 0, top = 0 } - end - break - end - end + set_position(wb, position) end --- Attach a wibox to a screen. @@ -111,104 +191,55 @@ end -- @param wb The wibox to attach. -- @param position The position of the wibox: top, bottom, left or right. -- @param screen The screen to attach to -function awfulwibox.attach(wb, position, screen) - screen = get_screen(screen) - -- Store wibox as attached in a weak-valued table - local wibox_prop_table - -- Start from end since we sometimes remove items - for i = #wiboxes, 1, -1 do - -- Since wiboxes are stored as weak value, they can disappear. - -- If they did, remove their entries - if wiboxes[i].wibox == nil then - table.remove(wiboxes, i) - elseif wiboxes[i].wibox == wb then - wibox_prop_table = wiboxes[i] - -- We could break here, but well, let's check if there is no other - -- table with their wiboxes been garbage collected. - end - end - - if not wibox_prop_table then - table.insert(wiboxes, setmetatable({ wibox = wb, position = position, screen = screen }, { __mode = 'v' })) - else - wibox_prop_table.position = position - end - - wb:connect_signal("property::width", wibox_update_strut) - wb:connect_signal("property::height", wibox_update_strut) - wb:connect_signal("property::visible", wibox_update_strut) - - wb:connect_signal("property::width", call_wibox_position_hook_on_prop_update) - wb:connect_signal("property::height", call_wibox_position_hook_on_prop_update) - wb:connect_signal("property::visible", call_wibox_position_hook_on_prop_update) - wb:connect_signal("property::border_width", call_wibox_position_hook_on_prop_update) +-- @deprecated awful.wibox.attach +function awfulwibox.attach(wb, position, screen) --luacheck: no unused args + util.deprecate("awful.wibox.attach is deprecated, use the 'attach' property".. + " of awful.placement. This method doesn't do anything anymore" + ) end --- Align a wibox. +-- +-- Supported alignment are: +-- +-- * top_left +-- * top_right +-- * bottom_left +-- * bottom_right +-- * left +-- * right +-- * top +-- * bottom +-- * centered +-- * center_vertical +-- * center_horizontal +-- -- @param wb The wibox. --- @param align The alignment: left, right or center. --- @param screen If the wibox is not attached to any screen, you can specify the --- screen where to align. -function awfulwibox.align(wb, align, screen) - screen = get_screen(screen) - local position = awfulwibox.get_position(wb) - local area = screen.workarea - - if position == "right" then - if align == "right" then - wb.y = area.y - elseif align == "left" then - wb.y = area.y + area.height - (wb.height + 2 * wb.border_width) - elseif align == "center" then - wb.y = area.y + round((area.height - wb.height) / 2) - end - elseif position == "left" then - if align == "right" then - wb.y = (area.y + area.height) - (wb.height + 2 * wb.border_width) - elseif align == "left" then - wb.y = area.y - elseif align == "center" then - wb.y = area.y + round((area.height - wb.height) / 2) - end - elseif position == "bottom" then - if align == "right" then - wb.x = area.x + area.width - (wb.width + 2 * wb.border_width) - elseif align == "left" then - wb.x = area.x - elseif align == "center" then - wb.x = area.x + round((area.width - wb.width) / 2) - end - elseif position == "top" then - if align == "right" then - wb.x = area.x + area.width - (wb.width + 2 * wb.border_width) - elseif align == "left" then - wb.x = area.x - elseif align == "center" then - wb.x = area.x + round((area.width - wb.width) / 2) - end +-- @param align The alignment +-- @param screen This argument is deprecated. It is not used. Use wb.screen +-- directly. +-- @deprecated awful.wibox.align +-- @see awful.placement.align +function awfulwibox.align(wb, align, screen) --luacheck: no unused args + if align == "center" then + util.deprecate("awful.wibox.align(wb, 'center' is deprecated, use 'centered'") + align = "centered" end - -- Update struts regardless of changes - wibox_update_strut(wb) + if screen then + util.deprecate("awful.wibox.align 'screen' argument is deprecated") + end + + attach(wb, align) end --- Stretch a wibox so it takes all screen width or height. --- @param wb The wibox. --- @param screen The screen to stretch on, or the wibox screen. -function awfulwibox.stretch(wb, screen) - if screen then - screen = get_screen(screen) - local position = awfulwibox.get_position(wb) - local area = screen.workarea - if position == "right" or position == "left" then - wb.height = area.height - (2 * wb.border_width) - wb.y = area.y - else - wb.width = area.width - (2 * wb.border_width) - wb.x = area.x - end - end -end +-- +-- **This function has been removed.** +-- +-- @deprecated awful.wibox.stretch +-- @see awful.placement +-- @see stretch --- Create a new wibox and attach it to a screen edge. -- @see wibox @@ -234,24 +265,24 @@ function awfulwibox.new(arg) -- Set default size if position == "left" or position == "right" then - arg.width = arg.width or round(beautiful.get_font_height(arg.font) * 1.5) + arg.width = arg.width or math.ceil(beautiful.get_font_height(arg.font) * 1.5) if arg.height then has_to_stretch = false if arg.screen then local hp = tostring(arg.height):match("(%d+)%%") if hp then - arg.height = round(screen.geometry.height * hp / 100) + arg.height = math.ceil(screen.geometry.height * hp / 100) end end end else - arg.height = arg.height or round(beautiful.get_font_height(arg.font) * 1.5) + arg.height = arg.height or math.ceil(beautiful.get_font_height(arg.font) * 1.5) if arg.width then has_to_stretch = false if arg.screen then local wp = tostring(arg.width):match("(%d+)%%") if wp then - arg.width = round(screen.geometry.width * wp / 100) + arg.width = math.ceil(screen.geometry.width * wp / 100) end end end @@ -259,36 +290,36 @@ function awfulwibox.new(arg) local w = wibox(arg) + w.screen = screen + w._stretch = arg.stretch == nil and has_to_stretch or arg.stretch + + w:add_signal("property::position") + w.get_position = get_position + w.set_position = set_position + + w:add_signal("property::stretch") + w.get_stretch = get_stretch + w.set_stretch = set_stretch + w.visible = true - awfulwibox.attach(w, position, screen) - if has_to_stretch then - awfulwibox.stretch(w, screen) - else - awfulwibox.align(w, arg.align, screen) - end + w:set_position(position) - awfulwibox.set_position(w, position, screen) + table.insert(wiboxes, w) + + w:connect_signal("property::visible", function() reattach(w) end) return w end -local function update_wiboxes_on_struts(c) - local struts = c:struts() - if struts.left ~= 0 or struts.right ~= 0 - or struts.top ~= 0 or struts.bottom ~= 0 then - update_all_wiboxes_position() - end -end - --- Hook registered to reset all wiboxes position. -capi.client.connect_signal("property::struts", update_wiboxes_on_struts) -capi.client.connect_signal("unmanage", update_wiboxes_on_struts) - capi.screen.connect_signal("removed", function(s) - for _, wprop in ipairs(wiboxes) do - if wprop.screen == s then - wprop.wibox.visible = false + for _, wibox in ipairs(wiboxes) do + if wibox.screen == s then + if wibox.detach_callback then + wibox.detach_callback() + end + + wibox.visible = false end end end) @@ -297,6 +328,8 @@ function awfulwibox.mt:__call(...) return awfulwibox.new(...) end +--@DOC_wibox_COMMON@ + return setmetatable(awfulwibox, awfulwibox.mt) -- vim: filetype=lua:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:textwidth=80 From 77380eb121241a98e18d91f8e289fdb999d3db2f Mon Sep 17 00:00:00 2001 From: Emmanuel Lepage Vallee Date: Mon, 9 May 2016 21:35:02 -0400 Subject: [PATCH 17/30] wibox: Move the documentation to a shared file To be re-used by wibars, tooltips and menus --- docs/common/wibox.ldoc | 224 +++++++++++++++++++++++++++++++++++++++++ lib/wibox/init.lua | 219 +--------------------------------------- 2 files changed, 229 insertions(+), 214 deletions(-) create mode 100644 docs/common/wibox.ldoc diff --git a/docs/common/wibox.ldoc b/docs/common/wibox.ldoc new file mode 100644 index 00000000..771d7409 --- /dev/null +++ b/docs/common/wibox.ldoc @@ -0,0 +1,224 @@ +--- Border width. +-- +-- **Signal:** +-- +-- * *property::border_width* +-- +-- @property border_width +-- @param integer + +--- Border color. +-- +-- Please note that this property only support string based 24 bit or 32 bit +-- colors: +-- +-- Red Blue +-- _| _| +-- #FF00FF +-- T‾ +-- Green +-- +-- +-- Red Blue +-- _| _| +-- #FF00FF00 +-- T‾ ‾T +-- Green Alpha +-- +-- **Signal:** +-- +-- * *property::border_color* +-- +-- @property border_color +-- @param string + +--- On top of other windows. +-- +-- **Signal:** +-- +-- * *property::ontop* +-- +-- @property ontop +-- @param boolean + +--- The mouse cursor. +-- +-- **Signal:** +-- +-- * *property::cursor* +-- +-- @property cursor +-- @param string +-- @see mouse + +--- Visibility. +-- +-- **Signal:** +-- +-- * *property::visible* +-- +-- @property visible +-- @param boolean + +--- The opacity of the wibox, between 0 and 1. +-- +-- **Signal:** +-- +-- * *property::opacity* +-- +-- @property opacity +-- @tparam number opacity (between 0 and 1) + +--- The window type (desktop, normal, dock, ...). +-- +-- **Signal:** +-- +-- * *property::type* +-- +-- @property type +-- @param string +-- @see client.type + +--- The x coordinates. +-- +-- **Signal:** +-- +-- * *property::x* +-- +-- @property x +-- @param integer + +--- The y coordinates. +-- +-- **Signal:** +-- +-- * *property::y* +-- +-- @property y +-- @param integer + +--- The width of the wibox. +-- +-- **Signal:** +-- +-- * *property::width* +-- +-- @property width +-- @param width + +--- The height of the wibox. +-- +-- **Signal:** +-- +-- * *property::height* +-- +-- @property height +-- @param height + +--- The wibox screen. +-- +-- @property screen +-- @param screen + +--- The wibox's `drawable`. +-- +-- **Signal:** +-- +-- * *property::drawable* +-- +-- @property drawable +-- @tparam drawable drawable + +--- The widget that the `wibox` displays. +-- @property widget +-- @param widget + +--- The X window id. +-- +-- **Signal:** +-- +-- * *property::window* +-- +-- @property window +-- @param string +-- @see client.window + +--- The wibox's bounding shape as a (native) cairo surface. +-- +-- **Signal:** +-- +-- * *property::shape_bounding* +-- +-- @property shape_bounding +-- @param surface._native + +--- The wibox's clip shape as a (native) cairo surface. +-- +-- **Signal:** +-- +-- * *property::shape_clip* +-- +-- @property shape_clip +-- @param surface._native + +--- Get or set mouse buttons bindings to a wibox. +-- +-- @param buttons_table A table of buttons objects, or nothing. +-- @function wibox.buttons + +--- Get or set wibox geometry. That's the same as accessing or setting the x, +-- y, width or height properties of a wibox. +-- +-- @param A table with coordinates to modify. +-- @return A table with wibox coordinates and geometry. +-- @function wibox.geometry + +--- Get or set wibox struts. +-- +-- @param strut A table with new strut, or nothing +-- @return The wibox strut in a table. +-- @function wibox.struts +-- @see client.struts + +--- The default background color. +-- @beautiful beautiful.bg_normal +-- @see bg + +--- The default foreground (text) color. +-- @beautiful beautiful.fg_normal +-- @see fg + +--- Set a declarative widget hierarchy description. +-- See [The declarative layout system](../documentation/03-declarative-layout.md.html) +-- @param args An array containing the widgets disposition +-- @name setup +-- @class function + +--- The background of the wibox. +-- @param c The background to use. This must either be a cairo pattern object, +-- nil or a string that gears.color() understands. +-- @property bg +-- @see gears.color + +--- The background image of the drawable. +-- If `image` is a function, it will be called with `(context, cr, width, height)` +-- as arguments. Any other arguments passed to this method will be appended. +-- @param image A background image or a function +-- @property bgimage +-- @see gears.surface + +--- The foreground (text) of the wibox. +-- @param c The foreground to use. This must either be a cairo pattern object, +-- nil or a string that gears.color() understands. +-- @property fg +-- @see gears.color + +--- Find a widget by a point. +-- The wibox must have drawn itself at least once for this to work. +-- @tparam number x X coordinate of the point +-- @tparam number y Y coordinate of the point +-- @treturn table A sorted table of widgets positions. The first element is the biggest +-- container while the last is the topmost widget. The table contains *x*, *y*, +-- *width*, *height* and *widget*. +-- @name find_widgets +-- @class function diff --git a/lib/wibox/init.lua b/lib/wibox/init.lua index 0a6bed50..638b478d 100644 --- a/lib/wibox/init.lua +++ b/lib/wibox/init.lua @@ -33,239 +33,30 @@ local force_forward = { shape_clip = true, } ---- Set the widget that the wibox displays +--@DOC_wibox_COMMON@ + function wibox:set_widget(widget) self._drawable:set_widget(widget) end --- Import some drawin documentation +function wibox:get_widget() + return self._drawable.widget +end ---- Border width. --- --- **Signal:** --- --- * *property::border_width* --- --- @property border_width --- @param integer - ---- Border color. --- --- Please note that this property only support string based 24 bit or 32 bit --- colors: --- --- Red Blue --- _| _| --- #FF00FF --- T‾ --- Green --- --- --- Red Blue --- _| _| --- #FF00FF00 --- T‾ ‾T --- Green Alpha --- --- **Signal:** --- --- * *property::border_color* --- --- @property border_color --- @param string - ---- On top of other windows. --- --- **Signal:** --- --- * *property::ontop* --- --- @property ontop --- @param boolean - ---- The mouse cursor. --- --- **Signal:** --- --- * *property::cursor* --- --- @property cursor --- @param string --- @see mouse - ---- Visibility. --- --- **Signal:** --- --- * *property::visible* --- --- @property visible --- @param boolean - ---- The opacity of the wibox, between 0 and 1. --- --- **Signal:** --- --- * *property::opacity* --- --- @property opacity --- @tparam number opacity (between 0 and 1) - ---- The window type (desktop, normal, dock, ...). --- --- **Signal:** --- --- * *property::type* --- --- @property type --- @param string --- @see client.type - ---- The x coordinates. --- --- **Signal:** --- --- * *property::x* --- --- @property x --- @param integer - ---- The y coordinates. --- --- **Signal:** --- --- * *property::y* --- --- @property y --- @param integer - ---- The width of the wibox. --- --- **Signal:** --- --- * *property::width* --- --- @property width --- @param width - ---- The height of the wibox. --- --- **Signal:** --- --- * *property::height* --- --- @property height --- @param height - ---- The wibox's `drawable`. --- --- **Signal:** --- --- * *property::drawable* --- --- @property drawable --- @tparam drawable drawable - ---- The X window id. --- --- **Signal:** --- --- * *property::window* --- --- @property window --- @param string --- @see client.window - ---- The wibox's bounding shape as a (native) cairo surface. --- --- **Signal:** --- --- * *property::shape_bounding* --- --- @property shape_bounding --- @param surface._native - ---- The wibox's clip shape as a (native) cairo surface. --- --- **Signal:** --- --- * *property::shape_clip* --- --- @property shape_clip --- @param surface._native - ---- Get or set mouse buttons bindings to a wibox. --- --- @param buttons_table A table of buttons objects, or nothing. --- @function wibox.buttons - ---- Get or set wibox geometry. That's the same as accessing or setting the x, --- y, width or height properties of a wibox. --- --- @param A table with coordinates to modify. --- @return A table with wibox coordinates and geometry. --- @function wibox.geometry - ---- Get or set wibox struts. --- --- @param strut A table with new strut, or nothing --- @return The wibox strut in a table. --- @function wibox.struts --- @see client.struts - ---- The default background color. --- @beautiful beautiful.bg_normal --- @see bg - ---- The default foreground (text) color. --- @beautiful beautiful.fg_normal --- @see fg - ---- Set a declarative widget hierarchy description. --- See [The declarative layout system](../documentation/03-declarative-layout.md.html) --- @param args An array containing the widgets disposition --- @name setup --- @class function wibox.setup = base.widget.setup ---- The background of the wibox. --- @param c The background to use. This must either be a cairo pattern object, --- nil or a string that gears.color() understands. --- @property bg --- @see gears.color - function wibox:set_bg(c) self._drawable:set_bg(c) end ---- The background image of the drawable. --- If `image` is a function, it will be called with `(context, cr, width, height)` --- as arguments. Any other arguments passed to this method will be appended. --- @param image A background image or a function --- @property bgimage --- @see gears.surface - function wibox:set_bgimage(image, ...) self._drawable:set_bgimage(image, ...) end ---- The foreground (text) of the wibox. --- @param c The foreground to use. This must either be a cairo pattern object, --- nil or a string that gears.color() understands. --- @property fg --- @see gears.color - function wibox:set_fg(c) self._drawable:set_fg(c) end ---- Find a widget by a point. --- The wibox must have drawn itself at least once for this to work. --- @tparam number x X coordinate of the point --- @tparam number y Y coordinate of the point --- @treturn table A sorted table of widgets positions. The first element is the biggest --- container while the last is the topmost widget. The table contains *x*, *y*, --- *width*, *height* and *widget*. function wibox:find_widgets(x, y) return self._drawable:find_widgets(x, y) end From 7d390a91f13b1811af5a8c1e736d06950bce146d Mon Sep 17 00:00:00 2001 From: Emmanuel Lepage Vallee Date: Sun, 8 May 2016 01:49:11 -0400 Subject: [PATCH 18/30] awful.wibox: Import some wibox documentation. --- lib/awful/wibox.lua | 2 ++ 1 file changed, 2 insertions(+) diff --git a/lib/awful/wibox.lua b/lib/awful/wibox.lua index fef12b33..ffaa64ea 100644 --- a/lib/awful/wibox.lua +++ b/lib/awful/wibox.lua @@ -24,6 +24,8 @@ local beautiful = require("beautiful") local util = require("awful.util") local placement = require("awful.placement") +--@DOC_wibox_COMMON@ + local function get_screen(s) return s and capi.screen[s] end From dafd29f2a374e145775daa77fc00a732d7169870 Mon Sep 17 00:00:00 2001 From: Emmanuel Lepage Vallee Date: Sun, 8 May 2016 02:10:35 -0400 Subject: [PATCH 19/30] awful.wibox: Rename to awful.wibar Why: * Two different (but related) concepts had the same name * Users were confused for years on IRC * The wibar name was already in use in some doc to avoid confusion --- awesomerc.lua | 2 +- lib/awful/init.lua | 1 + lib/awful/mouse/init.lua | 12 +- lib/awful/wibar.lua | 335 +++++++++++++++++++++++++++++++++++++++ lib/awful/wibox.lua | 329 ++------------------------------------ objects/screen.c | 2 +- 6 files changed, 356 insertions(+), 325 deletions(-) create mode 100644 lib/awful/wibar.lua diff --git a/awesomerc.lua b/awesomerc.lua index 7d511e0a..c50fb151 100755 --- a/awesomerc.lua +++ b/awesomerc.lua @@ -196,7 +196,7 @@ awful.screen.connect_for_each_screen(function(s) mytasklist[s] = awful.widget.tasklist(s, awful.widget.tasklist.filter.currenttags, mytasklist.buttons) -- Create the wibox - mywibox[s] = awful.wibox({ position = "top", screen = s }) + mywibox[s] = awful.wibar({ position = "top", screen = s }) -- Add widgets to the wibox mywibox[s]:setup { diff --git a/lib/awful/init.lua b/lib/awful/init.lua index ac639e15..0df325ac 100644 --- a/lib/awful/init.lua +++ b/lib/awful/init.lua @@ -51,6 +51,7 @@ return remote = require("awful.remote"); key = require("awful.key"); button = require("awful.button"); + wibar = require("awful.wibar"); wibox = require("awful.wibox"); startup_notification = require("awful.startup_notification"); tooltip = require("awful.tooltip"); diff --git a/lib/awful/mouse/init.lua b/lib/awful/mouse/init.lua index 9e094fb6..6acdb4eb 100644 --- a/lib/awful/mouse/init.lua +++ b/lib/awful/mouse/init.lua @@ -10,7 +10,7 @@ -- Grab environment we need local layout = require("awful.layout") local aplace = require("awful.placement") -local awibox = require("awful.wibox") +local awibar = require("awful.wibar") local util = require("awful.util") local type = type local ipairs = ipairs @@ -135,20 +135,20 @@ function mouse.wibox.move(w) capi.mousegrabber.run(function (_mouse) local button_down = false - if awibox.get_position(w) == "floating" then + if awibar.get_position(w) == "floating" then w.x = capi.mouse.coords().x + offset.x w.y = capi.mouse.coords().y + offset.y else local wa = capi.screen[capi.mouse.screen].workarea if capi.mouse.coords()["y"] > wa.y + wa.height - 10 then - awibox.set_position(w, "bottom", w.screen) + awibar.set_position(w, "bottom", w.screen) elseif capi.mouse.coords()["y"] < wa.y + 10 then - awibox.set_position(w, "top", w.screen) + awibar.set_position(w, "top", w.screen) elseif capi.mouse.coords()["x"] > wa.x + wa.width - 10 then - awibox.set_position(w, "right", w.screen) + awibar.set_position(w, "right", w.screen) elseif capi.mouse.coords()["x"] < wa.x + 10 then - awibox.set_position(w, "left", w.screen) + awibar.set_position(w, "left", w.screen) end w.screen = capi.mouse.screen end diff --git a/lib/awful/wibar.lua b/lib/awful/wibar.lua new file mode 100644 index 00000000..91b2bb5f --- /dev/null +++ b/lib/awful/wibar.lua @@ -0,0 +1,335 @@ +--------------------------------------------------------------------------- +--- Wibox module for awful. +-- This module allows you to easily create wibox and attach them to the edge of +-- a screen. +-- +-- @author Emmanuel Lepage Vallee <elv1313@gmail.com> +-- @copyright 2016 Emmanuel Lepage Vallee +-- @release @AWESOME_VERSION@ +-- @module awful.wibar +--------------------------------------------------------------------------- + +-- Grab environment we need +local capi = +{ + screen = screen, + client = client +} +local setmetatable = setmetatable +local tostring = tostring +local ipairs = ipairs +local error = error +local wibox = require("wibox") +local beautiful = require("beautiful") +local util = require("awful.util") +local placement = require("awful.placement") + +local function get_screen(s) + return s and capi.screen[s] +end + +local awfulwibar = { mt = {} } + +--- Array of table with wiboxes inside. +-- It's an array so it is ordered. +local wiboxes = setmetatable({}, {__mode = "v"}) + +-- Compute the margin on one side +local function get_margin(w, position, auto_stop) + local h_or_w = (position == "top" or position == "bottom") and "height" or "width" + local ret = 0 + + for _, v in ipairs(wiboxes) do + -- Ignore the wibars placed after this one + if auto_stop and v == w then break end + + if v.position == position and v.screen == w.screen and v.visible then + ret = ret + v[h_or_w] + end + end + + return ret +end + +-- `honor_workarea` cannot be used as it does modify the workarea itself. +-- a manual padding has to be generated. +local function get_margins(w) + local position = w.position + assert(position) + + local margins = {left=0, right=0, top=0, bottom=0} + + margins[position] = get_margin(w, position, true) + + -- Avoid overlapping wibars + if position == "left" or position == "right" then + margins.top = get_margin(w, "top" ) + margins.bottom = get_margin(w, "bottom") + end + + return margins +end + +-- Create the placement function +local function gen_placement(position, stretch) + local maximize = (position == "right" or position == "left") and + "maximize_vertically" or "maximize_horizontally" + + return placement[position] + (stretch and placement[maximize] or nil) +end + +-- Attach the placement function. +local function attach(wb, align) + gen_placement(align, wb._stretch)(wb, { + attach = true, + update_workarea = true, + margins = get_margins(wb) + }) +end + +-- Re-attach all wibars on a given wibar screen +local function reattach(wb) + local s = wb.screen + for _, w in ipairs(wiboxes) do + if w ~= wb and w.screen == s then + if w.detach_callback then + w.detach_callback() + w.detach_callback = nil + end + attach(w, w.position) + end + end +end + +--- The wibox position. +-- @property position +-- @param string Either "left", right", "top" or "bottom" + +local function get_position(wb) + return wb._position or "top" +end + +local function set_position(wb, position) + -- Detach first to avoid any uneeded callbacks + if wb.detach_callback then + wb.detach_callback() + + -- Avoid disconnecting twice, this produces a lot of warnings + wb.detach_callback = nil + end + + -- Move the wibar to the end of the list to avoid messing up the others in + -- case there is stacked wibars on one side. + if wb._position then + for k, w in ipairs(wiboxes) do + if w == wb then + table.remove(wiboxes, k) + end + end + table.insert(wiboxes, wb) + end + + -- In case the position changed, it may be necessary to reset the size + if (wb._position == "left" or wb._position == "right") + and (position == "top" or position == "bottom") then + wb.height = math.ceil(beautiful.get_font_height(wb.font) * 1.5) + elseif (wb._position == "top" or wb._position == "bottom") + and (position == "left" or position == "right") then + wb.width = math.ceil(beautiful.get_font_height(wb.font) * 1.5) + end + + -- Changing the position will also cause the other margins to be invalidated. + -- For example, adding a wibar to the top will change the margins of any left + -- or right wibars. To solve, this, they need to be re-attached. + reattach(wb) + + -- Set the new position + wb._position = position + + -- Attach to the new position + attach(wb, position) +end + +--- Stretch the wibar. +-- +-- @property stretch +-- @param[opt=true] boolean + +local function get_stretch(w) + return w._stretch +end + +local function set_stretch(w, value) + w._stretch = value + + attach(w, w.position) +end + +--- Get a wibox position if it has been set, or return top. +-- @param wb The wibox +-- @deprecated awful.wibar.get_position +-- @return The wibox position. +function awfulwibar.get_position(wb) + util.deprecate("Use wb:get_position() instead of awful.wibar.get_position") + return get_position(wb) +end + +--- Put a wibox on a screen at this position. +-- @param wb The wibox to attach. +-- @param position The position: top, bottom left or right. +-- @param screen This argument is deprecated, use wb.screen directly. +-- @deprecated awful.wibar.set_position +function awfulwibar.set_position(wb, position, screen) --luacheck: no unused args + util.deprecate("Use wb:set_position(position) instead of awful.wibar.set_position") + + set_position(wb, position) +end + +--- Attach a wibox to a screen. +-- If a wibox is attached, it will be automatically be moved when other wiboxes +-- will be attached. +-- @param wb The wibox to attach. +-- @param position The position of the wibox: top, bottom, left or right. +-- @param screen The screen to attach to +-- @deprecated awful.wibar.attach +function awfulwibar.attach(wb, position, screen) --luacheck: no unused args + util.deprecate("awful.wibar.attach is deprecated, use the 'attach' property".. + " of awful.placement. This method doesn't do anything anymore" + ) +end + +--- Align a wibox. +-- +-- Supported alignment are: +-- +-- * top_left +-- * top_right +-- * bottom_left +-- * bottom_right +-- * left +-- * right +-- * top +-- * bottom +-- * centered +-- * center_vertical +-- * center_horizontal +-- +-- @param wb The wibox. +-- @param align The alignment +-- @param screen This argument is deprecated. It is not used. Use wb.screen +-- directly. +-- @deprecated awful.wibar.align +-- @see awful.placement.align +function awfulwibar.align(wb, align, screen) --luacheck: no unused args + if align == "center" then + util.deprecate("awful.wibar.align(wb, 'center' is deprecated, use 'centered'") + align = "centered" + end + + if screen then + util.deprecate("awful.wibar.align 'screen' argument is deprecated") + end + + attach(wb, align) +end + +--- Stretch a wibox so it takes all screen width or height. +-- +-- **This function has been removed.** +-- +-- @deprecated awful.wibox.stretch +-- @see awful.placement +-- @see stretch + +--- Create a new wibox and attach it to a screen edge. +-- @see wibox +-- @param arg A table with standard arguments to wibox() creator. +-- You can add also position key with value top, bottom, left or right. +-- You can also use width or height in % and set align to center, right or left. +-- You can also set the screen key with a screen number to attach the wibox. +-- If not specified, 1 is assumed. +-- @return The wibox created. +function awfulwibar.new(arg) + arg = arg or {} + local position = arg.position or "top" + local has_to_stretch = true + local screen = get_screen(arg.screen or 1) + + arg.type = arg.type or "dock" + + if position ~= "top" and position ~="bottom" + and position ~= "left" and position ~= "right" then + error("Invalid position in awful.wibar(), you may only use" + .. " 'top', 'bottom', 'left' and 'right'") + end + + -- Set default size + if position == "left" or position == "right" then + arg.width = arg.width or math.ceil(beautiful.get_font_height(arg.font) * 1.5) + if arg.height then + has_to_stretch = false + if arg.screen then + local hp = tostring(arg.height):match("(%d+)%%") + if hp then + arg.height = math.ceil(screen.geometry.height * hp / 100) + end + end + end + else + arg.height = arg.height or math.ceil(beautiful.get_font_height(arg.font) * 1.5) + if arg.width then + has_to_stretch = false + if arg.screen then + local wp = tostring(arg.width):match("(%d+)%%") + if wp then + arg.width = math.ceil(screen.geometry.width * wp / 100) + end + end + end + end + + local w = wibox(arg) + + w.screen = screen + w._stretch = arg.stretch == nil and has_to_stretch or arg.stretch + + w:add_signal("property::position") + w.get_position = get_position + w.set_position = set_position + + w:add_signal("property::stretch") + w.get_stretch = get_stretch + w.set_stretch = set_stretch + + w.visible = true + + w:set_position(position) + + table.insert(wiboxes, w) + + w:connect_signal("property::visible", function() reattach(w) end) + + return w +end + +capi.screen.connect_signal("removed", function(s) + for _, wibar in ipairs(wiboxes) do + if wibar.screen == s then + if wibar.detach_callback then + wibar.detach_callback() + end + + wibar.visible = false + end + end +end) + +function awfulwibar.mt:__call(...) + return awfulwibar.new(...) +end + +--@DOC_wibox_COMMON@ + +return setmetatable(awfulwibar, awfulwibar.mt) + +-- vim: filetype=lua:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:textwidth=80 diff --git a/lib/awful/wibox.lua b/lib/awful/wibox.lua index ffaa64ea..276b9f38 100644 --- a/lib/awful/wibox.lua +++ b/lib/awful/wibox.lua @@ -1,337 +1,32 @@ --------------------------------------------------------------------------- ---- Wibox module for awful. --- This module allows you to easily create wibox and attach them to the edge of --- a screen. +--- This module is deprecated and has been renamed `awful.wibar` -- -- @author Emmanuel Lepage Vallee <elv1313@gmail.com> -- @copyright 2016 Emmanuel Lepage Vallee -- @release @AWESOME_VERSION@ -- @module awful.wibox --------------------------------------------------------------------------- - --- Grab environment we need -local capi = -{ - screen = screen, - client = client -} -local setmetatable = setmetatable -local tostring = tostring -local ipairs = ipairs -local error = error -local wibox = require("wibox") -local beautiful = require("beautiful") local util = require("awful.util") -local placement = require("awful.placement") +local wibar = require("awful.wibar") ---@DOC_wibox_COMMON@ +local function call(_,...) + util.deprecate("awful.wibox has been renamed to awful.wibar") -local function get_screen(s) - return s and capi.screen[s] + return wibar(...) end -local awfulwibox = { mt = {} } +local function index(_, k) + util.deprecate("awful.wibox has been renamed to awful.wibar") ---- Array of table with wiboxes inside. --- It's an array so it is ordered. -local wiboxes = setmetatable({}, {__mode = "v"}) - --- Compute the margin on one side -local function get_margin(w, position, auto_stop) - local h_or_w = (position == "top" or position == "bottom") and "height" or "width" - local ret = 0 - - for _, v in ipairs(wiboxes) do - -- Ignore the wiboxes placed after this one - if auto_stop and v == w then break end - - if v.position == position and v.screen == w.screen and v.visible then - ret = ret + v[h_or_w] - end - end - - return ret + return wibar[k] end --- `honor_workarea` cannot be used as it does modify the workarea itself. --- a manual padding has to be generated. -local function get_margins(w) - local position = w.position - assert(position) +local function newindex(_, k, v) + util.deprecate("awful.wibox has been renamed to awful.wibar") - local margins = {left=0, right=0, top=0, bottom=0} - - margins[position] = get_margin(w, position, true) - - -- Avoid overlapping wiboxes - if position == "left" or position == "right" then - margins.top = get_margin(w, "top" ) - margins.bottom = get_margin(w, "bottom") - end - - return margins + wibar[k] = v end --- Create the placement function -local function gen_placement(position, stretch) - local maximize = (position == "right" or position == "left") and - "maximize_vertically" or "maximize_horizontally" - - return placement[position] + (stretch and placement[maximize] or nil) -end - --- Attach the placement function. -local function attach(wb, align) - gen_placement(align, wb._stretch)(wb, { - attach = true, - update_workarea = true, - margins = get_margins(wb) - }) -end - --- Re-attach all wiboxes on a given wibox screen -local function reattach(wb) - local s = wb.screen - for k, w in ipairs(wiboxes) do - if w ~= wb and w.screen == s then - if w.detach_callback then - w.detach_callback() - w.detach_callback = nil - end - attach(w, w.position) - end - end -end - ---- The wibox position. --- @property position --- @param string Either "left", right", "top" or "bottom" - -local function get_position(wb) - return wb._position or "top" -end - -local function set_position(wb, position) - -- Detach first to avoid any uneeded callbacks - if wb.detach_callback then - wb.detach_callback() - - -- Avoid disconnecting twice, this produces a lot of warnings - wb.detach_callback = nil - end - - -- Move the wibox to the end of the list to avoid messing up the others in - -- case there is stacked wiboxes on one side. - if wb._position then - for k, w in ipairs(wiboxes) do - if w == wb then - table.remove(wiboxes, k) - end - end - table.insert(wiboxes, wb) - end - - -- In case the position changed, it may be necessary to reset the size - if (wb._position == "left" or wb._position == "right") - and (position == "top" or position == "bottom") then - wb.height = math.ceil(beautiful.get_font_height(wb.font) * 1.5) - elseif (wb._position == "top" or wb._position == "bottom") - and (position == "left" or position == "right") then - wb.width = math.ceil(beautiful.get_font_height(wb.font) * 1.5) - end - - -- Changing the position will also cause the other margins to be invalidated. - -- For example, adding a wibox to the top will change the margins of any left - -- or right wiboxes. To solve, this, they need to be re-attached. - reattach(wb) - - -- Set the new position - wb._position = position - - -- Attach to the new position - attach(wb, position) -end - ---- Stretch the wibox. --- --- @property stretch --- @param[opt=true] boolean - -local function get_stretch(w) - return w._stretch -end - -local function set_stretch(w, value) - w._stretch = value - - attach(w, w.position) -end - ---- Get a wibox position if it has been set, or return top. --- @param wb The wibox --- @deprecated awful.wibox.get_position --- @return The wibox position. -function awfulwibox.get_position(wb) - util.deprecate("Use wb:get_position() instead of awful.wibox.get_position") - return get_position(wb) -end - ---- Put a wibox on a screen at this position. --- @param wb The wibox to attach. --- @param position The position: top, bottom left or right. --- @param screen This argument is deprecated, use wb.screen directly. --- @deprecated awful.wibox.set_position -function awfulwibox.set_position(wb, position, screen) --luacheck: no unused args - util.deprecate("Use wb:set_position(position) instead of awful.wibox.set_position") - - set_position(wb, position) -end - ---- Attach a wibox to a screen. --- If a wibox is attached, it will be automatically be moved when other wiboxes --- will be attached. --- @param wb The wibox to attach. --- @param position The position of the wibox: top, bottom, left or right. --- @param screen The screen to attach to --- @deprecated awful.wibox.attach -function awfulwibox.attach(wb, position, screen) --luacheck: no unused args - util.deprecate("awful.wibox.attach is deprecated, use the 'attach' property".. - " of awful.placement. This method doesn't do anything anymore" - ) -end - ---- Align a wibox. --- --- Supported alignment are: --- --- * top_left --- * top_right --- * bottom_left --- * bottom_right --- * left --- * right --- * top --- * bottom --- * centered --- * center_vertical --- * center_horizontal --- --- @param wb The wibox. --- @param align The alignment --- @param screen This argument is deprecated. It is not used. Use wb.screen --- directly. --- @deprecated awful.wibox.align --- @see awful.placement.align -function awfulwibox.align(wb, align, screen) --luacheck: no unused args - if align == "center" then - util.deprecate("awful.wibox.align(wb, 'center' is deprecated, use 'centered'") - align = "centered" - end - - if screen then - util.deprecate("awful.wibox.align 'screen' argument is deprecated") - end - - attach(wb, align) -end - ---- Stretch a wibox so it takes all screen width or height. --- --- **This function has been removed.** --- --- @deprecated awful.wibox.stretch --- @see awful.placement --- @see stretch - ---- Create a new wibox and attach it to a screen edge. --- @see wibox --- @param arg A table with standard arguments to wibox() creator. --- You can add also position key with value top, bottom, left or right. --- You can also use width or height in % and set align to center, right or left. --- You can also set the screen key with a screen number to attach the wibox. --- If not specified, 1 is assumed. --- @return The wibox created. -function awfulwibox.new(arg) - arg = arg or {} - local position = arg.position or "top" - local has_to_stretch = true - local screen = get_screen(arg.screen or 1) - - arg.type = arg.type or "dock" - - if position ~= "top" and position ~="bottom" - and position ~= "left" and position ~= "right" then - error("Invalid position in awful.wibox(), you may only use" - .. " 'top', 'bottom', 'left' and 'right'") - end - - -- Set default size - if position == "left" or position == "right" then - arg.width = arg.width or math.ceil(beautiful.get_font_height(arg.font) * 1.5) - if arg.height then - has_to_stretch = false - if arg.screen then - local hp = tostring(arg.height):match("(%d+)%%") - if hp then - arg.height = math.ceil(screen.geometry.height * hp / 100) - end - end - end - else - arg.height = arg.height or math.ceil(beautiful.get_font_height(arg.font) * 1.5) - if arg.width then - has_to_stretch = false - if arg.screen then - local wp = tostring(arg.width):match("(%d+)%%") - if wp then - arg.width = math.ceil(screen.geometry.width * wp / 100) - end - end - end - end - - local w = wibox(arg) - - w.screen = screen - w._stretch = arg.stretch == nil and has_to_stretch or arg.stretch - - w:add_signal("property::position") - w.get_position = get_position - w.set_position = set_position - - w:add_signal("property::stretch") - w.get_stretch = get_stretch - w.set_stretch = set_stretch - - w.visible = true - - w:set_position(position) - - table.insert(wiboxes, w) - - w:connect_signal("property::visible", function() reattach(w) end) - - return w -end - -capi.screen.connect_signal("removed", function(s) - for _, wibox in ipairs(wiboxes) do - if wibox.screen == s then - if wibox.detach_callback then - wibox.detach_callback() - end - - wibox.visible = false - end - end -end) - -function awfulwibox.mt:__call(...) - return awfulwibox.new(...) -end - ---@DOC_wibox_COMMON@ - -return setmetatable(awfulwibox, awfulwibox.mt) +return setmetatable({}, {__call = call, __index = index, __newindex = newindex}) -- vim: filetype=lua:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:textwidth=80 diff --git a/objects/screen.c b/objects/screen.c index 42207cf7..e01a2f8d 100644 --- a/objects/screen.c +++ b/objects/screen.c @@ -117,7 +117,7 @@ * The screen workarea. * * The workarea is a subsection of the screen where clients can be placed. It - * usually excludes the toolbars (see `awful.wibox`) and dockable clients + * usually excludes the toolbars (see `awful.wibar`) and dockable clients * (see `client.dockable`) like WindowMaker DockAPP. * * It can be modified be altering the `wibox` or `client` struts. From 38185b161a16552ba3fe683b429c10e0e620e72b Mon Sep 17 00:00:00 2001 From: Emmanuel Lepage Vallee Date: Sun, 15 May 2016 03:50:39 -0400 Subject: [PATCH 20/30] wibar: Add a remove function --- lib/awful/wibar.lua | 31 ++++++++++++++++++++++++------- 1 file changed, 24 insertions(+), 7 deletions(-) diff --git a/lib/awful/wibar.lua b/lib/awful/wibar.lua index 91b2bb5f..45602011 100644 --- a/lib/awful/wibar.lua +++ b/lib/awful/wibar.lua @@ -6,7 +6,7 @@ -- @author Emmanuel Lepage Vallee <elv1313@gmail.com> -- @copyright 2016 Emmanuel Lepage Vallee -- @release @AWESOME_VERSION@ --- @module awful.wibar +-- @classmod awful.wibar --------------------------------------------------------------------------- -- Grab environment we need @@ -165,6 +165,25 @@ local function set_stretch(w, value) attach(w, w.position) end +--- Remove a wibar. +-- @function remove +local function remove(self) + self.visible = false + + if self.detach_callback then + self.detach_callback() + self.detach_callback = nil + end + + for k, w in ipairs(wiboxes) do + if w == self then + table.remove(wiboxes, k) + end + end + + self._screen = nil +end + --- Get a wibox position if it has been set, or return top. -- @param wb The wibox -- @deprecated awful.wibar.get_position @@ -291,6 +310,7 @@ function awfulwibar.new(arg) local w = wibox(arg) w.screen = screen + w._screen = screen --HACK When a screen is removed, then getbycoords wont work w._stretch = arg.stretch == nil and has_to_stretch or arg.stretch w:add_signal("property::position") @@ -300,6 +320,7 @@ function awfulwibar.new(arg) w:add_signal("property::stretch") w.get_stretch = get_stretch w.set_stretch = set_stretch + w.remove = remove w.visible = true @@ -314,12 +335,8 @@ end capi.screen.connect_signal("removed", function(s) for _, wibar in ipairs(wiboxes) do - if wibar.screen == s then - if wibar.detach_callback then - wibar.detach_callback() - end - - wibar.visible = false + if wibar._screen == s then + wibar:remove() end end end) From 4b395bea811b70fd556407cb0e51108f37b96997 Mon Sep 17 00:00:00 2001 From: Emmanuel Lepage Vallee Date: Sun, 8 May 2016 02:25:19 -0400 Subject: [PATCH 21/30] mouse: Port wibox.move to the placement API --- lib/awful/mouse/init.lua | 49 +++++++++++++++------------------------- 1 file changed, 18 insertions(+), 31 deletions(-) diff --git a/lib/awful/mouse/init.lua b/lib/awful/mouse/init.lua index 6acdb4eb..2c9e1c94 100644 --- a/lib/awful/mouse/init.lua +++ b/lib/awful/mouse/init.lua @@ -10,7 +10,6 @@ -- Grab environment we need local layout = require("awful.layout") local aplace = require("awful.placement") -local awibar = require("awful.wibar") local util = require("awful.util") local type = type local ipairs = ipairs @@ -123,43 +122,31 @@ end --- Move the wibox under the cursor. -- @function awful.mouse.wibox.move ---@param w The wibox to move, or none to use that under the pointer +--@tparam wibox w The wibox to move, or none to use that under the pointer function mouse.wibox.move(w) w = w or mouse.wibox_under_pointer() if not w then return end + if not w + or w.type == "desktop" + or w.type == "splash" + or w.type == "dock" then + return + end + + -- Compute the offset + local coords = capi.mouse.coords() + local geo = aplace.centered(capi.mouse,{parent=w, pretend=true}) + local offset = { - x = w.x - capi.mouse.coords().x, - y = w.y - capi.mouse.coords().y + x = geo.x - coords.x, + y = geo.y - coords.y, } - capi.mousegrabber.run(function (_mouse) - local button_down = false - if awibar.get_position(w) == "floating" then - w.x = capi.mouse.coords().x + offset.x - w.y = capi.mouse.coords().y + offset.y - else - local wa = capi.screen[capi.mouse.screen].workarea - - if capi.mouse.coords()["y"] > wa.y + wa.height - 10 then - awibar.set_position(w, "bottom", w.screen) - elseif capi.mouse.coords()["y"] < wa.y + 10 then - awibar.set_position(w, "top", w.screen) - elseif capi.mouse.coords()["x"] > wa.x + wa.width - 10 then - awibar.set_position(w, "right", w.screen) - elseif capi.mouse.coords()["x"] < wa.x + 10 then - awibar.set_position(w, "left", w.screen) - end - w.screen = capi.mouse.screen - end - for _, v in ipairs(_mouse.buttons) do - if v then button_down = true end - end - if not button_down then - return false - end - return true - end, "fleur") + mouse.resize(w, "mouse.move", { + placement = aplace.under_mouse, + offset = offset + }) end --- Get a client corner coordinates. From 52208bc728e250504f804018d06d7e94b872657d Mon Sep 17 00:00:00 2001 From: Emmanuel Lepage Vallee Date: Sat, 7 May 2016 03:24:06 -0400 Subject: [PATCH 22/30] tests: Test wibox attachment, wibars and struts --- tests/test-struts.lua | 322 ++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 322 insertions(+) create mode 100644 tests/test-struts.lua diff --git a/tests/test-struts.lua b/tests/test-struts.lua new file mode 100644 index 00000000..00363ff2 --- /dev/null +++ b/tests/test-struts.lua @@ -0,0 +1,322 @@ +local placement = require("awful.placement") +local wibox = require("wibox") +local wibar = require("awful.wibar") + +-- luacheck: globals mywibox +local steps = {} + +local parent, small + +local twibar, bwibar, lwibar, rwibar = mywibox[screen.primary] + +-- Test the struts without using wibars +table.insert(steps, function() + local sgeo = screen.primary.geometry + + -- Manually place at the bottom right + local w = wibox{ + height = 50, + width = 50, + y = sgeo.y + sgeo.height - 50, + x = sgeo.x + sgeo.width - 50, + visible = true, + } + + w:struts { + left = 0, + top = 0, + bottom = 50, + right = 50, + } + + local wa = screen.primary.workarea + + assert(wa.x == 0 ) + assert(wa.y == twibar.height ) + assert(wa.width == sgeo.width - 50 ) + assert(wa.height == sgeo.height - 50 - twibar.height) + + w:struts { + left = 0, + top = 0, + bottom = 0, + right = 0, + } + w.visible = false + + wa = screen.primary.workarea + + assert(wa.x == 0 ) + assert(wa.y == twibar.height ) + assert(wa.width == sgeo.width ) + assert(wa.height == sgeo.height - twibar.height) + + return true +end) + +-- Test "attach" for wibox/client to wibox +table.insert(steps, function() + parent = wibox { + visible = true, + width = 500, + height = 500, + } + + placement.centered(parent) + + small = wibox { + bg = "#ff0000", + height = 24, + ontop = true, + visible = true, + } + + -- Add an attached function + placement.top_left(small, {parent = parent}) + placement.maximize_horizontally(small, {parent = parent, attach = true}) + + return true +end) + +table.insert(steps, function() + assert(parent:geometry().width == 500) + assert(parent:geometry().height == 500) + + assert(parent:geometry().y == small:geometry().y) + assert(parent:geometry().x == small:geometry().x) + assert(parent:geometry().width == small:geometry().width ) + assert(small:geometry().height == 24) + + -- Now, move the parent and see of the attached one is correctly updated + placement.stretch_left(parent) + + return true +end) + +table.insert(steps, function() + assert(parent:geometry().y == small:geometry().y) + assert(parent:geometry().x == small:geometry().x) + assert(parent:geometry().width == small:geometry().width ) + assert(small:geometry().height == 24) + + -- Do the same, but with placement compositing + small = wibox { + bg = "#ff0000", + height = 50, + width = 50, + ontop = true, + visible = true, + } + + local p = placement.bottom_right + placement.scale + + p(small, {parent=parent, attach=true, direction="left", to_percent=0.5}) + + return true +end) + +local function check_ratio() + assert( + ( + parent:geometry().y + + parent:geometry().height + - small :geometry().height + ) == small:geometry().y + ) + + assert(math.abs(parent:geometry().x + math.ceil(parent:geometry().width/2) - small:geometry().x) <= 1) + assert(math.abs(math.ceil(parent:geometry().width/2) - small:geometry().width) <= 1) + assert(small:geometry().height == 50) +end + +table.insert(steps, function() + check_ratio() + + -- Now, do some more transformation on the parent and make sure the "small" + -- wibox follow. + placement.scale (parent, {direction="left", to_percent=0.2}) + placement.scale (parent, {direction="up" , to_percent=0.2}) + placement.bottom (parent ) + placement.top_right(parent ) + placement.centered (parent ) + + return true +end) + +-- Do the same checks to see if everything has followed as expected +table.insert(steps, function() + check_ratio() + + -- Now, test if the wibar has updated the workarea. + placement.maximize(parent, {honor_workarea = true}) + + return true +end) + +table.insert(steps, function() + local wa = screen.primary.workarea + local sgeo = screen.primary.geometry + local wgeo = twibar:geometry() + + assert(wa.width == sgeo.width ) + assert(wa.x == sgeo.x ) + assert(wa.y == sgeo.y + wgeo.height ) +-- assert(wa.height == sgeo.height - wgeo.height) +-- assert(wgeo.height == sgeo.height - wa.height ) + + assert(parent.y == wa.y ) + assert(parent.x == wa.x ) + assert(parent.width == wa.width ) + assert(parent.height == wa.height ) + + -- Add more wibars + bwibar = wibar {position = "bottom", bg = "#00ff00"} + lwibar = wibar {position = "left" , bg = "#0000ff"} + rwibar = wibar {position = "right" , bg = "#ff00ff"} + + return true +end) + +-- Make sure the maximized client has the right size and position +local function check_maximize() + local pgeo = parent:geometry() + + local margins = {left=0, right=0, top=0, bottom=0} + + for _, w in ipairs {twibar, lwibar, rwibar, bwibar} do + if w.visible then + local pos = w.position + local w_or_h = (pos == "left" or pos == "right") and "width" or "height" + margins[pos] = margins[pos] + w[w_or_h] + end + end + + local sgeo = parent.screen.geometry + + sgeo.x = sgeo.x + margins.left + sgeo.y = sgeo.y + margins.top + sgeo.width = sgeo.width - margins.left - margins.right + sgeo.height = sgeo.height - margins.top - margins.bottom + + local wa = parent.screen.workarea + + for k, v in pairs(wa) do + assert(sgeo[k] == v) + end + + assert(sgeo.width == pgeo.width + 2*parent.border_width) + assert(sgeo.height == pgeo.height + 2*parent.border_width) + assert(sgeo.x == pgeo.x ) + assert(sgeo.y == pgeo.y ) +end + +table.insert(steps, function() + -- Attach the parent wibox to it is updated along the workarea + placement.maximize(parent, {honor_workarea = true, attach = true}) + + -- Make the wibox more visible + parent.border_color = "#ffff00" + parent.border_width = 10 + parent.ontop = true +-- parent.visible = false + + local wa = screen.primary.workarea + local sgeo = screen.primary.geometry + + assert(lwibar.width == rwibar.width ) + assert(lwibar.height == rwibar.height ) + assert(twibar.x == bwibar.x ) + assert(twibar.width == bwibar.width ) + + -- Check the left wibar size and position + assert(lwibar.x == wa.x - lwibar.width ) + assert(lwibar.height == wa.height ) + + -- Check the right wibar size and position + assert(rwibar.x == wa.x + wa.width ) + assert(rwibar.height == wa.height ) + + -- Check the bottom wibar size and position + assert(bwibar.width == sgeo.width ) + assert(bwibar.x == 0 ) + + return true +end) + +table.insert(steps, function() + check_maximize() + + -- There should be a detach callback + assert(lwibar.detach_callback) + + -- Begin to move wibars around + lwibar.position = "top" + assert(lwibar.position == "top") + assert(lwibar.y == twibar.height) + + return true +end) + +table.insert(steps, function() + check_maximize() + + bwibar.position = "right" + bwibar.ontop = true + rwibar.ontop = true + assert(bwibar.position == "right") + + return true +end) + +table.insert(steps, function() + check_maximize() + + rwibar.position = "top" + + return true +end) + +table.insert(steps, function() + check_maximize() + + bwibar.position = "top" + + return true +end) + +table.insert(steps, function() + check_maximize() + + for _, w in ipairs {twibar, lwibar, rwibar, bwibar} do + w.position = "right" + end + + return true +end) + +-- Test visibility +table.insert(steps, function() + check_maximize() + + twibar.visible = false + rwibar.visible = false + + return true +end) + +table.insert(steps, function() + check_maximize() + + twibar.visible = true + + return true +end) + +table.insert(steps, function() + check_maximize() + + return true +end) + + +require("_runner").run_steps(steps) From b8920c2a7a556a24c8093bc1c2c050d80ce64988 Mon Sep 17 00:00:00 2001 From: Emmanuel Lepage Vallee Date: Sun, 15 May 2016 23:44:09 -0400 Subject: [PATCH 23/30] doc: `for s, screen do` -> `for s in screen do` --- objects/screen.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/objects/screen.c b/objects/screen.c index e01a2f8d..0791ea31 100644 --- a/objects/screen.c +++ b/objects/screen.c @@ -30,7 +30,7 @@ * * It is also possible loop over all current screens using: * - * for s, screen do + * for s in screen do * -- do something * end * From 9d13e08c63d26b74a4b88084cc91ff2afcc5b02a Mon Sep 17 00:00:00 2001 From: Emmanuel Lepage Vallee Date: Sun, 15 May 2016 23:44:54 -0400 Subject: [PATCH 24/30] gears: Add a geometry modules Add an abstract geometry module to `gears` --- lib/gears/geometry.lua | 60 ++++++++++++++++++++++++++++++++++++++++++ lib/gears/init.lua | 1 + 2 files changed, 61 insertions(+) create mode 100644 lib/gears/geometry.lua diff --git a/lib/gears/geometry.lua b/lib/gears/geometry.lua new file mode 100644 index 00000000..b578eea8 --- /dev/null +++ b/lib/gears/geometry.lua @@ -0,0 +1,60 @@ +--------------------------------------------------------------------------- +-- +-- Helper functions used to compute geometries. +-- +-- When this module refer to a geometry table, this assume a table with at least +-- an *x*, *y*, *width* and *height* keys and numeric values. +-- +-- @author Julien Danjou <julien@danjou.info> +-- @copyright 2008 Julien Danjou +-- @release @AWESOME_VERSION@ +-- @module gears.geometry +--------------------------------------------------------------------------- +local math = math + +local gears = {geometry = {rectangle = {} } } + +--- Get the square distance between a rectangle and a point. +-- @tparam table geom A rectangle +-- @tparam number geom.x The horizontal coordinate +-- @tparam number geom.y The vertical coordinate +-- @tparam number geom.width The rectangle width +-- @tparam number geom.height The rectangle height +-- @tparam number x X coordinate of point +-- @tparam number y Y coordinate of point +-- @treturn number The squared distance of the rectangle to the provided point +function gears.geometry.rectangle.get_square_distance(geom, x, y) + local dist_x, dist_y = 0, 0 + if x < geom.x then + dist_x = geom.x - x + elseif x >= geom.x + geom.width then + dist_x = x - geom.x - geom.width + 1 + end + if y < geom.y then + dist_y = geom.y - y + elseif y >= geom.y + geom.height then + dist_y = y - geom.y - geom.height + 1 + end + return dist_x * dist_x + dist_y * dist_y +end + +--- Return the closest rectangle from `list` for a given point. +-- @tparam table list A list of geometry tables. +-- @tparam number x The x coordinate +-- @tparam number y The y coordinate +-- @return The key from the closest geometry. +function gears.geometry.rectangle.get_closest_by_coord(list, x, y) + local dist = math.huge + local ret = nil + + for k, v in pairs(list) do + local d = gears.geometry.rectangle.get_square_distance(v, x, y) + if d < dist then + ret, dist = k, d + end + end + + return ret +end + +return gears.geometry diff --git a/lib/gears/init.lua b/lib/gears/init.lua index 3fe46b44..2d35de36 100644 --- a/lib/gears/init.lua +++ b/lib/gears/init.lua @@ -19,6 +19,7 @@ return shape = require("gears.shape"); protected_call = require("gears.protected_call"); screen = require("gears.screen"); + geometry = require("gears.geometry"); } -- vim: filetype=lua:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:textwidth=80 From 251614afff2eb41e168781339af0c4de560a6441 Mon Sep 17 00:00:00 2001 From: Emmanuel Lepage Vallee Date: Sun, 15 May 2016 23:46:11 -0400 Subject: [PATCH 25/30] wibox: Remove the dependency on `awful` --- lib/wibox/init.lua | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/lib/wibox/init.lua b/lib/wibox/init.lua index 638b478d..7e56f8c2 100644 --- a/lib/wibox/init.lua +++ b/lib/wibox/init.lua @@ -15,9 +15,9 @@ local setmetatable = setmetatable local pairs = pairs local type = type local object = require("gears.object") +local grect = require("gears.geometry").rectangle local beautiful = require("beautiful") local base = require("wibox.widget.base") -local ascreen = require("awful.screen") --- This provides widget box windows. Every wibox can also be used as if it were -- a drawin. All drawin functions and properties are also available on wiboxes! @@ -62,7 +62,13 @@ function wibox:find_widgets(x, y) end function wibox:get_screen() - return capi.screen[ascreen.getbycoord(self.x, self.y)] + local sgeos = {} + + for s in capi.screen do + sgeos[s] = s.geometry + end + + return grect.get_closest_by_coord(sgeos, self.x, self.y) end function wibox:set_screen(s) From dd121623b514d67c48b7aa7008ff276b4504d370 Mon Sep 17 00:00:00 2001 From: Emmanuel Lepage Vallee Date: Sun, 15 May 2016 23:59:59 -0400 Subject: [PATCH 26/30] gears.geometry: Mutualize getbycoord --- lib/gears/geometry.lua | 19 +++++++++++++++++++ lib/wibox/drawable.lua | 24 ++++++++++-------------- 2 files changed, 29 insertions(+), 14 deletions(-) diff --git a/lib/gears/geometry.lua b/lib/gears/geometry.lua index b578eea8..8fcf7c47 100644 --- a/lib/gears/geometry.lua +++ b/lib/gears/geometry.lua @@ -57,4 +57,23 @@ function gears.geometry.rectangle.get_closest_by_coord(list, x, y) return ret end +--- Return the rectangle containing the [x, y] point. +-- +-- Note that if multiple element from the geometry list contains the point, the +-- returned result is nondeterministic. +-- +-- @tparam table list A list of geometry tables. +-- @tparam number x The x coordinate +-- @tparam number y The y coordinate +-- @return The key from the closest geometry. In case no result is found, *nil* +-- is returned. +function gears.geometry.rectangle.get_by_coord(list, x, y) + for k, geometry in pairs(list) do + if x >= geometry.x and x < geometry.x + geometry.width + and y >= geometry.y and y < geometry.y + geometry.height then + return k + end + end +end + return gears.geometry diff --git a/lib/wibox/drawable.lua b/lib/wibox/drawable.lua index 9d8627cf..1f2c038e 100644 --- a/lib/wibox/drawable.lua +++ b/lib/wibox/drawable.lua @@ -19,30 +19,26 @@ local color = require("gears.color") local object = require("gears.object") local surface = require("gears.surface") local timer = require("gears.timer") +local grect = require("gears.geometry").rectangle local matrix = require("gears.matrix") local hierarchy = require("wibox.hierarchy") local unpack = unpack or table.unpack -- luacheck: globals unpack (compatibility with Lua 5.1) local drawables = setmetatable({}, { __mode = 'k' }) --- This is awful.screen.getbycoord() which we sadly cannot use from here (cyclic --- dependencies are bad!) -local function screen_getbycoord(x, y) - for i in screen do - local geometry = screen[i].geometry - if x >= geometry.x and x < geometry.x + geometry.width - and y >= geometry.y and y < geometry.y + geometry.height then - return capi.screen[i] - end - end - return capi.screen.primary -end - -- Get the widget context. This should always return the same table (if -- possible), so that our draw and fit caches can work efficiently. local function get_widget_context(self) local geom = self.drawable:geometry() - local s = screen_getbycoord(geom.x, geom.y) + + local sgeos = {} + + for s in capi.screen do + sgeos[s] = s.geometry + end + + local s = grect.get_by_coord(sgeos, geom.x, geom.y) or capi.screen.primary + local context = self._widget_context local dpi = beautiful.xresources.get_dpi(s) if (not context) or context.screen ~= s or context.dpi ~= dpi then From 1a05d53cab47fdca5dd0edf3713efb3c1770d11b Mon Sep 17 00:00:00 2001 From: Emmanuel Lepage Vallee Date: Mon, 16 May 2016 00:30:44 -0400 Subject: [PATCH 27/30] screen: Use `gears.geometry` --- lib/awful/screen.lua | 38 ++++++++++++-------------------------- 1 file changed, 12 insertions(+), 26 deletions(-) diff --git a/lib/awful/screen.lua b/lib/awful/screen.lua index b2f2543e..b98316fb 100644 --- a/lib/awful/screen.lua +++ b/lib/awful/screen.lua @@ -16,6 +16,7 @@ local capi = } local util = require("awful.util") local object = require("gears.object") +local grect = require("gears.geometry").rectangle local function get_screen(s) return s and capi.screen[s] @@ -62,20 +63,7 @@ end -- @tparam number y Y coordinate of point -- @treturn number The squared distance of the screen to the provided point function screen.object.get_square_distance(self, x, y) - self = get_screen(self) - local geom = self.geometry - local dist_x, dist_y = 0, 0 - if x < geom.x then - dist_x = geom.x - x - elseif x >= geom.x + geom.width then - dist_x = x - geom.x - geom.width + 1 - end - if y < geom.y then - dist_y = geom.y - y - elseif y >= geom.y + geom.height then - dist_y = y - geom.y - geom.height + 1 - end - return dist_x * dist_x + dist_y * dist_y + return grect.get_square_distance(get_screen(self).geometry, x, y) end --- @@ -83,20 +71,18 @@ end -- The number returned can be used as an index into the global -- `screen` table/object. -- @function awful.screen.getbycoord --- @param x The x coordinate --- @param y The y coordinate +-- @tparam number x The x coordinate +-- @tparam number y The y coordinate +-- @treturn ?number The screen index function screen.getbycoord(x, y) - local dist = math.huge - local s = capi.screen.primary - if s then - dist = screen.object.get_square_distance(s, x, y) - end - for i in capi.screen do - local d = screen.object.get_square_distance(i, x, y) - if d < dist then - s, dist = capi.screen[i], d - end + local s, sgeos = capi.screen.primary, {} + + for scr in capi.screen do + sgeos[scr] = scr.geometry end + + s = grect.get_closest_by_coord(sgeos, x, y) or s + return s and s.index end From 1ce92bb5506ac8c63705827be8e0e8639043130b Mon Sep 17 00:00:00 2001 From: Emmanuel Lepage Vallee Date: Mon, 16 May 2016 00:57:15 -0400 Subject: [PATCH 28/30] get_rect_by_dir: Move to `gears.geometry` Begin to break down `awful.util` --- lib/awful/client.lua | 3 +- lib/awful/client/focus.lua | 6 ++-- lib/awful/screen.lua | 2 +- lib/awful/util.lua | 67 +++-------------------------------- lib/gears/geometry.lua | 72 ++++++++++++++++++++++++++++++++++++++ 5 files changed, 83 insertions(+), 67 deletions(-) diff --git a/lib/awful/client.lua b/lib/awful/client.lua index bbc008a7..e4fed3cd 100644 --- a/lib/awful/client.lua +++ b/lib/awful/client.lua @@ -11,6 +11,7 @@ local util = require("awful.util") local spawn = require("awful.spawn") local object = require("gears.object") +local grect = require("gears.geometry").rectangle local pairs = pairs local type = type local ipairs = ipairs @@ -197,7 +198,7 @@ function client.swap.bydirection(dir, c, stacked) for i,cl in ipairs(cltbl) do geomtbl[i] = cl:geometry() end - local target = util.get_rectangle_in_direction(dir, geomtbl, sel:geometry()) + local target = grect.get_in_direction(dir, geomtbl, sel:geometry()) -- If we found a client to swap with, then go for it if target then diff --git a/lib/awful/client/focus.lua b/lib/awful/client/focus.lua index 9847d144..8243f6ba 100644 --- a/lib/awful/client/focus.lua +++ b/lib/awful/client/focus.lua @@ -6,7 +6,7 @@ -- @release @AWESOME_VERSION@ -- @submodule client --------------------------------------------------------------------------- -local util = require("awful.util") +local grect = require("gears.geometry").rectangle local capi = { @@ -168,7 +168,7 @@ function focus.bydirection(dir, c, stacked) geomtbl[i] = cl:geometry() end - local target = util.get_rectangle_in_direction(dir, geomtbl, sel:geometry()) + local target = grect.get_in_direction(dir, geomtbl, sel:geometry()) -- If we found a client to focus, then do it. if target then @@ -200,7 +200,7 @@ function focus.global_bydirection(dir, c, stacked) for i,cl in ipairs(cltbl) do geomtbl[i] = cl:geometry() end - local target = util.get_rectangle_in_direction(dir, geomtbl, scr.geometry) + local target = grect.get_in_direction(dir, geomtbl, scr.geometry) if target then cltbl[target]:emit_signal("request::activate", diff --git a/lib/awful/screen.lua b/lib/awful/screen.lua index b98316fb..cc384f91 100644 --- a/lib/awful/screen.lua +++ b/lib/awful/screen.lua @@ -137,7 +137,7 @@ function screen.focus_bydirection(dir, _screen) for s in capi.screen do geomtbl[s] = capi.screen[s].geometry end - local target = util.get_rectangle_in_direction(dir, geomtbl, sel.geometry) + local target = grect.get_in_direction(dir, geomtbl, sel.geometry) if target then return screen.focus(target) end diff --git a/lib/awful/util.lua b/lib/awful/util.lua index c64c43e2..18587522 100644 --- a/lib/awful/util.lua +++ b/lib/awful/util.lua @@ -20,6 +20,7 @@ local type = type local rtable = table local string = string local lgi = require("lgi") +local grect = require("gears.geometry").rectangle local Gio = require("lgi").Gio local Pango = lgi.Pango local capi = @@ -301,76 +302,18 @@ function util.subsets(set) return ret end ---- Return true whether rectangle B is in the right direction --- compared to rectangle A. --- @param dir The direction. --- @param gA The geometric specification for rectangle A. --- @param gB The geometric specification for rectangle B. --- @return True if B is in the direction of A. -local function is_in_direction(dir, gA, gB) - if dir == "up" then - return gA.y > gB.y - elseif dir == "down" then - return gA.y < gB.y - elseif dir == "left" then - return gA.x > gB.x - elseif dir == "right" then - return gA.x < gB.x - end - return false -end - ---- Calculate distance between two points. --- i.e: if we want to move to the right, we will take the right border --- of the currently focused screen and the left side of the checked screen. --- @param dir The direction. --- @param _gA The first rectangle. --- @param _gB The second rectangle. --- @return The distance between the screens. -local function calculate_distance(dir, _gA, _gB) - local gAx = _gA.x - local gAy = _gA.y - local gBx = _gB.x - local gBy = _gB.y - - if dir == "up" then - gBy = _gB.y + _gB.height - elseif dir == "down" then - gAy = _gA.y + _gA.height - elseif dir == "left" then - gBx = _gB.x + _gB.width - elseif dir == "right" then - gAx = _gA.x + _gA.width - end - - return math.sqrt(math.pow(gBx - gAx, 2) + math.pow(gBy - gAy, 2)) -end - --- Get the nearest rectangle in the given direction. Every rectangle is specified as a table -- with 'x', 'y', 'width', 'height' keys, the same as client or screen geometries. +-- @deprecated awful.util.get_rectangle_in_direction -- @param dir The direction, can be either "up", "down", "left" or "right". -- @param recttbl A table of rectangle specifications. -- @param cur The current rectangle. -- @return The index for the rectangle in recttbl closer to cur in the given direction. nil if none found. +-- @see gears.geometry function util.get_rectangle_in_direction(dir, recttbl, cur) - local dist, dist_min - local target = nil + util.deprecate("gears.geometry.rectangle.get_in_direction") - -- We check each object - for i, rect in pairs(recttbl) do - -- Check geometry to see if object is located in the right direction. - if is_in_direction(dir, cur, rect) then - -- Calculate distance between current and checked object. - dist = calculate_distance(dir, cur, rect) - - -- If distance is shorter then keep the object. - if not target or dist < dist_min then - target = i - dist_min = dist - end - end - end - return target + return grect.get_in_direction(dir, recttbl, cur) end --- Join all tables given as parameters. diff --git a/lib/gears/geometry.lua b/lib/gears/geometry.lua index 8fcf7c47..3e98fef8 100644 --- a/lib/gears/geometry.lua +++ b/lib/gears/geometry.lua @@ -76,4 +76,76 @@ function gears.geometry.rectangle.get_by_coord(list, x, y) end end +--- Return true whether rectangle B is in the right direction +-- compared to rectangle A. +-- @param dir The direction. +-- @param gA The geometric specification for rectangle A. +-- @param gB The geometric specification for rectangle B. +-- @return True if B is in the direction of A. +local function is_in_direction(dir, gA, gB) + if dir == "up" then + return gA.y > gB.y + elseif dir == "down" then + return gA.y < gB.y + elseif dir == "left" then + return gA.x > gB.x + elseif dir == "right" then + return gA.x < gB.x + end + return false +end + +--- Calculate distance between two points. +-- i.e: if we want to move to the right, we will take the right border +-- of the currently focused screen and the left side of the checked screen. +-- @param dir The direction. +-- @param _gA The first rectangle. +-- @param _gB The second rectangle. +-- @return The distance between the screens. +local function calculate_distance(dir, _gA, _gB) + local gAx = _gA.x + local gAy = _gA.y + local gBx = _gB.x + local gBy = _gB.y + + if dir == "up" then + gBy = _gB.y + _gB.height + elseif dir == "down" then + gAy = _gA.y + _gA.height + elseif dir == "left" then + gBx = _gB.x + _gB.width + elseif dir == "right" then + gAx = _gA.x + _gA.width + end + + return math.sqrt(math.pow(gBx - gAx, 2) + math.pow(gBy - gAy, 2)) +end + +--- Get the nearest rectangle in the given direction. Every rectangle is specified as a table +-- with *x*, *y*, *width*, *height* keys, the same as client or screen geometries. +-- @tparam string dir The direction, can be either *up*, *down*, *left* or *right*. +-- @tparam table recttbl A table of rectangle specifications. +-- @tparam table cur The current rectangle. +-- @return The index for the rectangle in recttbl closer to cur in the given direction. nil if none found. +function gears.geometry.rectangle.get_in_direction(dir, recttbl, cur) + local dist, dist_min + local target = nil + + -- We check each object + for i, rect in pairs(recttbl) do + -- Check geometry to see if object is located in the right direction. + if is_in_direction(dir, cur, rect) then + -- Calculate distance between current and checked object. + dist = calculate_distance(dir, cur, rect) + + -- If distance is shorter then keep the object. + if not target or dist < dist_min then + target = i + dist_min = dist + end + end + end + return target +end + return gears.geometry From aa46b96369eefac0793901f44f5b7749dbe7ee0d Mon Sep 17 00:00:00 2001 From: Emmanuel Lepage Vallee Date: Mon, 16 May 2016 01:07:58 -0400 Subject: [PATCH 29/30] gears.geometry: Add area_remove --- lib/awful/placement.lua | 81 +------------------------------------ lib/gears/geometry.lua | 90 +++++++++++++++++++++++++++++++++++++++++ 2 files changed, 92 insertions(+), 79 deletions(-) diff --git a/lib/awful/placement.lua b/lib/awful/placement.lua index be462a16..5d2c59ae 100644 --- a/lib/awful/placement.lua +++ b/lib/awful/placement.lua @@ -93,6 +93,7 @@ local capi = 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 dpi = require("beautiful").xresources.apply_dpi @@ -584,84 +585,6 @@ attach = function(d, position_f, args) end end ---- Check if an area intersect another area. --- @param a The area. --- @param b The other area. --- @return True if they intersect, false otherwise. -local function area_intersect_area(a, b) - return (b.x < a.x + a.width - and b.x + b.width > a.x - and b.y < a.y + a.height - and b.y + b.height > a.y) -end - ---- Get the intersect area between a and b. --- @param a The area. --- @param b The other area. --- @return The intersect area. -local function area_intersect_area_get(a, b) - local g = {} - g.x = math.max(a.x, b.x) - g.y = math.max(a.y, b.y) - g.width = math.min(a.x + a.width, b.x + b.width) - g.x - g.height = math.min(a.y + a.height, b.y + b.height) - g.y - return g -end - ---- Remove an area from a list, splitting the space between several area that --- can overlap. --- @param areas Table of areas. --- @param elem Area to remove. --- @return The new area list. -local function area_remove(areas, elem) - for i = #areas, 1, -1 do - -- Check if the 'elem' intersect - if area_intersect_area(areas[i], elem) then - -- It does? remove it - local r = table.remove(areas, i) - local inter = area_intersect_area_get(r, elem) - - if inter.x > r.x then - table.insert(areas, { - x = r.x, - y = r.y, - width = inter.x - r.x, - height = r.height - }) - end - - if inter.y > r.y then - table.insert(areas, { - x = r.x, - y = r.y, - width = r.width, - height = inter.y - r.y - }) - end - - if inter.x + inter.width < r.x + r.width then - table.insert(areas, { - x = inter.x + inter.width, - y = r.y, - width = (r.x + r.width) - (inter.x + inter.width), - height = r.height - }) - end - - if inter.y + inter.height < r.y + r.height then - table.insert(areas, { - x = r.x, - y = inter.y + inter.height, - width = r.width, - height = (r.y + r.height) - (inter.y + inter.height) - }) - end - end - end - - return areas -end - -- Convert 2 points into a rectangle local function rect_from_points(p1x, p1y, p2x, p2y) return { @@ -779,7 +702,7 @@ function placement.no_overlap(c) 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 = area_remove(areas, area_common(cl)) + areas = grect.area_remove(areas, area_common(cl)) end end diff --git a/lib/gears/geometry.lua b/lib/gears/geometry.lua index 3e98fef8..c70ec207 100644 --- a/lib/gears/geometry.lua +++ b/lib/gears/geometry.lua @@ -148,4 +148,94 @@ function gears.geometry.rectangle.get_in_direction(dir, recttbl, cur) return target end +--- Check if an area intersect another area. +-- @param a The area. +-- @param b The other area. +-- @return True if they intersect, false otherwise. +local function area_intersect_area(a, b) + return (b.x < a.x + a.width + and b.x + b.width > a.x + and b.y < a.y + a.height + and b.y + b.height > a.y) +end + +--- Get the intersect area between a and b. +-- @tparam table a The area. +-- @tparam number a.x The horizontal coordinate +-- @tparam number a.y The vertical coordinate +-- @tparam number a.width The rectangle width +-- @tparam number a.height The rectangle height +-- @tparam table b The other area. +-- @tparam number b.x The horizontal coordinate +-- @tparam number b.y The vertical coordinate +-- @tparam number b.width The rectangle width +-- @tparam number b.height The rectangle height +-- @treturn table The intersect area. +function gears.geometry.rectangle.get_intersection(a, b) + local g = {} + g.x = math.max(a.x, b.x) + g.y = math.max(a.y, b.y) + g.width = math.min(a.x + a.width, b.x + b.width) - g.x + g.height = math.min(a.y + a.height, b.y + b.height) - g.y + return g +end + +--- Remove an area from a list, splitting the space between several area that +-- can overlap. +-- @tparam table areas Table of areas. +-- @tparam table elem Area to remove. +-- @tparam number elem.x The horizontal coordinate +-- @tparam number elem.y The vertical coordinate +-- @tparam number elem.width The rectangle width +-- @tparam number elem.height The rectangle height +-- @return The new area list. +function gears.geometry.rectangle.area_remove(areas, elem) + for i = #areas, 1, -1 do + -- Check if the 'elem' intersect + if area_intersect_area(areas[i], elem) then + -- It does? remove it + local r = table.remove(areas, i) + local inter = gears.geometry.rectangle.get_intersection(r, elem) + + if inter.x > r.x then + table.insert(areas, { + x = r.x, + y = r.y, + width = inter.x - r.x, + height = r.height + }) + end + + if inter.y > r.y then + table.insert(areas, { + x = r.x, + y = r.y, + width = r.width, + height = inter.y - r.y + }) + end + + if inter.x + inter.width < r.x + r.width then + table.insert(areas, { + x = inter.x + inter.width, + y = r.y, + width = (r.x + r.width) - (inter.x + inter.width), + height = r.height + }) + end + + if inter.y + inter.height < r.y + r.height then + table.insert(areas, { + x = r.x, + y = inter.y + inter.height, + width = r.width, + height = (r.y + r.height) - (inter.y + inter.height) + }) + end + end + end + + return areas +end + return gears.geometry From 11e77d51914402b947b544a2eb6d025c4a3e0d9a Mon Sep 17 00:00:00 2001 From: Emmanuel Lepage Vallee Date: Mon, 16 May 2016 01:16:55 -0400 Subject: [PATCH 30/30] gears: Remove `screen` It has been deprecated for a while and was never part of a release. --- lib/gears/init.lua | 1 - lib/gears/screen.lua | 29 ----------------------------- 2 files changed, 30 deletions(-) delete mode 100644 lib/gears/screen.lua diff --git a/lib/gears/init.lua b/lib/gears/init.lua index 2d35de36..3cad460d 100644 --- a/lib/gears/init.lua +++ b/lib/gears/init.lua @@ -18,7 +18,6 @@ return matrix = require("gears.matrix"); shape = require("gears.shape"); protected_call = require("gears.protected_call"); - screen = require("gears.screen"); geometry = require("gears.geometry"); } diff --git a/lib/gears/screen.lua b/lib/gears/screen.lua deleted file mode 100644 index 0baadf83..00000000 --- a/lib/gears/screen.lua +++ /dev/null @@ -1,29 +0,0 @@ ---------------------------------------------------------------------------- --- @author Uli Schlachter --- @copyright 2016 Uli Schlachter --- @release @AWESOME_VERSION@ --- @classmod gears.screen ---------------------------------------------------------------------------- - -local ascreen = require("awful.screen") -local util = require("awful.util") - -local module = {} - ---- Call a function for each existing and created-in-the-future screen. --- @tparam function func The function to call. -function module.connect_for_each_screen(func) - util.deprecate("Use awful.screen.connect_for_each_screen") - ascreen.connect_for_each_screen(func) -end - ---- Undo the effect of connect_for_each_screen. --- @tparam function func The function that should no longer be called. -function module.disconnect_for_each_screen(func) - util.deprecate("Use awful.screen.disconnect_for_each_screen") - ascreen.disconnect_for_each_screen(func) -end - -return module - --- vim: filetype=lua:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:textwidth=80