diff --git a/lib/awful/client.lua b/lib/awful/client.lua index 688e64603..41feccded 100644 --- a/lib/awful/client.lua +++ b/lib/awful/client.lua @@ -10,6 +10,7 @@ -- Grab environment we need local util = require("awful.util") local spawn = require("awful.spawn") +local object = require("gears.object") local tag = require("awful.tag") local pairs = pairs local type = type @@ -1104,6 +1105,7 @@ capi.client.add_signal("marked") capi.client.add_signal("unmarked") capi.client.connect_signal("focus", client.focus.history.add) + -- Add clients during startup to focus history. -- This used to happen through ewmh.activate, but that only handles visible -- clients now. @@ -1123,6 +1125,14 @@ capi.client.connect_signal("unmanage", client.floating.delete) -- Register persistent properties client.property.persist("floating", "boolean") +-- Extend the luaobject +object.properties(capi.client, { + getter_class = client, + setter_class = client, + getter_fallback = client.property.get, + setter_fallback = client.property.set, +}) + return client -- vim: filetype=lua:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:textwidth=80 diff --git a/lib/awful/screen.lua b/lib/awful/screen.lua index 2855d9025..220185f88 100644 --- a/lib/awful/screen.lua +++ b/lib/awful/screen.lua @@ -15,6 +15,7 @@ local capi = client = client } local util = require("awful.util") +local object = require("gears.object") local function get_screen(s) return s and capi.screen[s] @@ -245,6 +246,9 @@ end capi.screen.add_signal("padding") +-- Extend the luaobject +object.properties(capi.screen, {auto_emit=true}) + return screen -- vim: filetype=lua:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:textwidth=80 diff --git a/lib/awful/tag.lua b/lib/awful/tag.lua index e51c3a828..1a8b0a7ae 100644 --- a/lib/awful/tag.lua +++ b/lib/awful/tag.lua @@ -11,6 +11,7 @@ local util = require("awful.util") local ascreen = require("awful.screen") local beautiful = require("beautiful") +local object = require("gears.object") local pairs = pairs local ipairs = ipairs local table = table @@ -873,6 +874,16 @@ function tag.mt:__call(...) return tag.new(...) end +-- Extend the luaobject +-- `awful.tag.setproperty` currently handle calling the setter method itself +-- while `awful.tag.getproperty`. +object.properties(capi.tag, { + getter_class = tag, + setter_class = tag, + getter_fallback = tag.getproperty, + setter = tag.setproperty, +}) + return setmetatable(tag, tag.mt) -- vim: filetype=lua:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:textwidth=80 diff --git a/lib/gears/object.lua b/lib/gears/object.lua index 2eb230cdb..9c21e6810 100644 --- a/lib/gears/object.lua +++ b/lib/gears/object.lua @@ -9,8 +9,9 @@ local setmetatable = setmetatable local pairs = pairs local type = type local error = error +local properties = require("gears.object.properties") -local object = { mt = {} } +local object = { properties = properties, mt = {} } --- Verify that obj is indeed a valid object as returned by new() local function check(obj) diff --git a/lib/gears/object/properties.lua b/lib/gears/object/properties.lua new file mode 100644 index 000000000..7d05ca865 --- /dev/null +++ b/lib/gears/object/properties.lua @@ -0,0 +1,103 @@ +--------------------------------------------------------------------------- +--- An helper module to map userdata __index and __newindex entries to +-- lua classes. +-- +-- @author Emmanuel Lepage-Vallee <elv1313@gmail.com> +-- @copyright 2016 Emmanuel Lepage-Vallee +-- @release @AWESOME_VERSION@ +-- @module gears.object.properties +--------------------------------------------------------------------------- + +local object = {} + +local properties = setmetatable({}, { __mode = 'k' }) + +local function cobj_register(cobj) + local fallback = {} + + function fallback:rawset(_, prop, val) + fallback[prop] = val + end + + function fallback:rawget(_, prop) + return fallback[prop] + end + + properties[cobj] = fallback + return fallback +end + +--- Add the missing properties handler to a CAPI object such as client/tag/screen +-- Valid args: +-- +-- * **getter**: A smart getter (handle property getter itself) +-- * **getter_fallback**: A dumb getter method (don't handle individual property getter) +-- * **getter_class**: A module with individual property getter/setter +-- * **getter_prefix**: A special getter prefix (like "get" or "get_" (default)) +-- * **setter**: A smart setter (handle property setter itself) +-- * **setter_fallback**: A dumb setter method (don't handle individual property setter) +-- * **setter_class**: A module with individual property getter/setter +-- * **setter_prefix**: A special setter prefix (like "set" or "set_" (default)) +-- * **auto_emit**: Emit "property::___" automatically (default: false). This is +-- ignored when setter_fallback is set or a setter is found +-- +-- @param class A standard luaobject derived object +-- @tparam[opt={}] table args A set of accessors configuration parameters +function object.capi_index_fallback(class, args) + args = args or {} + + local getter_prefix = args.getter_prefix or "get_" + local setter_prefix = args.setter_prefix or "set_" + + local getter = args.getter or function(cobj, prop) + -- Look for a getter method + if args.getter_class and args.getter_class[getter_prefix..prop] then + return args.getter_class[getter_prefix..prop](cobj) + end + + -- Make sure something like c:a_mutator() works + if args.getter_class and args.getter_class[prop] then + return args.getter_class[prop] + end + + -- In case there is already a "dumb" getter like `awful.tag.getproperty' + if args.getter_fallback then + return args.getter_fallback(cobj, prop) + end + + local fallback = properties[cobj] or cobj_register(cobj) + + -- Use the fallback property table + return fallback[prop] + end + + local setter = args.setter or function(cobj, prop, value) + -- Look for a setter method + if args.setter_class and args.setter_class[setter_prefix..prop] then + return args.setter_class[setter_prefix..prop](cobj, value) + end + + -- In case there is already a "dumb" setter like `awful.client.property.set' + if args.setter_fallback then + return args.setter_fallback(cobj, prop, value) + end + + local fallback = properties[cobj] or cobj_register(cobj) + + -- Use the fallback property table + fallback[prop] = value + + -- Emit the signal + if args.auto_emit then + cobj:emit_signal("property::"..prop, value) + end + end + + -- Attach the accessor methods + class.set_index_miss_handler(getter) + class.set_newindex_miss_handler(setter) +end + +return setmetatable( object, {__call = function(_,...) object.capi_index_fallback(...) end}) + +-- vim: filetype=lua:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:textwidth=80 diff --git a/tests/test-miss-handlers.lua b/tests/test-miss-handlers.lua index 0d9e3a0ed..f97d089b2 100644 --- a/tests/test-miss-handlers.lua +++ b/tests/test-miss-handlers.lua @@ -2,6 +2,7 @@ local class = tag local obj = class({}) +local handler = require("gears.object.properties") awesome.connect_signal("debug::index::miss", error) awesome.connect_signal("debug::newindex::miss", error) @@ -23,4 +24,10 @@ end) obj.key = 42 assert(called) +handler(class, {auto_emit=true}) + +assert(not obj.key) +obj.key = 1337 +assert(obj.key == 1337) + require("_runner").run_steps({ function() return true end })